]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/padovan/blueto...
authorJohn W. Linville <linville@tuxdriver.com>
Tue, 6 Dec 2011 21:02:05 +0000 (16:02 -0500)
committerJohn W. Linville <linville@tuxdriver.com>
Tue, 6 Dec 2011 21:02:05 +0000 (16:02 -0500)
1  2 
drivers/bluetooth/btmrvl_sdio.c
include/net/bluetooth/bluetooth.h
include/net/bluetooth/hci_core.h
net/bluetooth/l2cap_sock.c
net/bluetooth/mgmt.c

index 9ef48167e2cf8ba64c790d4846a2d297e84c6fd0,37b56398c8a6699d29e64bf87d2d1500d1d6f383..27b74b0d547b540043fd318917e2cb5d6ebe0591
@@@ -23,7 -23,6 +23,7 @@@
  
  #include <linux/mmc/sdio_ids.h>
  #include <linux/mmc/sdio_func.h>
 +#include <linux/module.h>
  
  #include <net/bluetooth/bluetooth.h>
  #include <net/bluetooth/hci_core.h>
@@@ -65,7 -64,7 +65,7 @@@ static const struct btmrvl_sdio_card_re
        .io_port_1 = 0x01,
        .io_port_2 = 0x02,
  };
- static const struct btmrvl_sdio_card_reg btmrvl_reg_8787 = {
+ static const struct btmrvl_sdio_card_reg btmrvl_reg_87xx = {
        .cfg = 0x00,
        .host_int_mask = 0x02,
        .host_intstatus = 0x03,
@@@ -92,7 -91,14 +92,14 @@@ static const struct btmrvl_sdio_device 
  static const struct btmrvl_sdio_device btmrvl_sdio_sd8787 = {
        .helper         = NULL,
        .firmware       = "mrvl/sd8787_uapsta.bin",
-       .reg            = &btmrvl_reg_8787,
+       .reg            = &btmrvl_reg_87xx,
+       .sd_blksz_fw_dl = 256,
+ };
+ static const struct btmrvl_sdio_device btmrvl_sdio_sd8797 = {
+       .helper         = NULL,
+       .firmware       = "mrvl/sd8797_uapsta.bin",
+       .reg            = &btmrvl_reg_87xx,
        .sd_blksz_fw_dl = 256,
  };
  
@@@ -103,6 -109,9 +110,9 @@@ static const struct sdio_device_id btmr
        /* Marvell SD8787 Bluetooth device */
        { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x911A),
                        .driver_data = (unsigned long) &btmrvl_sdio_sd8787 },
+       /* Marvell SD8797 Bluetooth device */
+       { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x912A),
+                       .driver_data = (unsigned long) &btmrvl_sdio_sd8797 },
  
        { }     /* Terminating entry */
  };
@@@ -1076,3 -1085,4 +1086,4 @@@ MODULE_LICENSE("GPL v2")
  MODULE_FIRMWARE("sd8688_helper.bin");
  MODULE_FIRMWARE("sd8688.bin");
  MODULE_FIRMWARE("mrvl/sd8787_uapsta.bin");
+ MODULE_FIRMWARE("mrvl/sd8797_uapsta.bin");
index 835f3b229b8450d53f2ec434078dd9a3c0e8be52,97264fc8feebf3a017e70930383eed3027febc07..980e59f37d4f8cb6b487570189b328683d311b5b
  #define PF_BLUETOOTH  AF_BLUETOOTH
  #endif
  
+ /* Bluetooth versions */
+ #define BLUETOOTH_VER_1_1     1
+ #define BLUETOOTH_VER_1_2     2
+ #define BLUETOOTH_VER_2_0     3
  /* Reserv for core and drivers use */
  #define BT_SKB_RESERVE        8
  
@@@ -104,7 -109,7 +109,7 @@@ struct bt_power 
   */
  #define BT_CHANNEL_POLICY_AMP_PREFERRED               2
  
 -__attribute__((format (printf, 2, 3)))
 +__printf(2, 3)
  int bt_printk(const char *level, const char *fmt, ...);
  
  #define BT_INFO(fmt, arg...)   bt_printk(KERN_INFO, pr_fmt(fmt), ##arg)
index f333e7682607b1dbcc86871e3d0fd6c93a94c6cf,e34cd71a586eba54d810d77cfc4693ef81cfea18..ea4395f1d26000214027baee3ed401353cb671b3
@@@ -170,6 -170,8 +170,8 @@@ struct hci_dev 
        __u32           amp_max_flush_to;
        __u32           amp_be_flush_to;
  
+       __u8            flow_ctl_mode;
        unsigned int    auto_accept_delay;
  
        unsigned long   quirks;
  
        struct module           *owner;
  
+       unsigned long           dev_flags;
        int (*open)(struct hci_dev *hdev);
        int (*close)(struct hci_dev *hdev);
        int (*flush)(struct hci_dev *hdev);
@@@ -572,15 -576,11 +576,15 @@@ static inline void __hci_dev_put(struc
                d->destruct(d);
  }
  
 -static inline void hci_dev_put(struct hci_dev *d)
 -{
 -      __hci_dev_put(d);
 -      module_put(d->owner);
 -}
 +/*
 + * hci_dev_put and hci_dev_hold are macros to avoid dragging all the
 + * overhead of all the modular infrastructure into this header.
 + */
 +#define hci_dev_put(d)                \
 +do {                          \
 +      __hci_dev_put(d);       \
 +      module_put(d->owner);   \
 +} while (0)
  
  static inline struct hci_dev *__hci_dev_hold(struct hci_dev *d)
  {
        return d;
  }
  
 -static inline struct hci_dev *hci_dev_hold(struct hci_dev *d)
 -{
 -      if (try_module_get(d->owner))
 -              return __hci_dev_hold(d);
 -      return NULL;
 -}
 +#define hci_dev_hold(d)                                               \
 +({                                                            \
 +      try_module_get(d->owner) ? __hci_dev_hold(d) : NULL;    \
 +})
  
  #define hci_dev_lock(d)               spin_lock(&d->lock)
  #define hci_dev_unlock(d)     spin_unlock(&d->lock)
@@@ -917,11 -919,13 +921,13 @@@ int mgmt_connectable(struct hci_dev *hd
  int mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status);
  int mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key,
                                                                u8 persistent);
- int mgmt_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type);
- int mgmt_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type);
- int mgmt_disconnect_failed(struct hci_dev *hdev);
- int mgmt_connect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type,
-                                                               u8 status);
+ int mgmt_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
+                                                               u8 addr_type);
+ int mgmt_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
+                                                               u8 addr_type);
+ int mgmt_disconnect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 status);
+ int mgmt_connect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
+                                               u8 addr_type, u8 status);
  int mgmt_pin_code_request(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 secure);
  int mgmt_pin_code_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
                                                                u8 status);
@@@ -933,14 -937,20 +939,20 @@@ int mgmt_user_confirm_reply_complete(st
                                                                u8 status);
  int mgmt_user_confirm_neg_reply_complete(struct hci_dev *hdev,
                                                bdaddr_t *bdaddr, u8 status);
+ int mgmt_user_passkey_request(struct hci_dev *hdev, bdaddr_t *bdaddr);
+ int mgmt_user_passkey_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
+                                                               u8 status);
+ int mgmt_user_passkey_neg_reply_complete(struct hci_dev *hdev,
+                                               bdaddr_t *bdaddr, u8 status);
  int mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 status);
  int mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status);
  int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash,
                                                u8 *randomizer, u8 status);
- int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type,
-                                       u8 *dev_class, s8 rssi, u8 *eir);
+ int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
+                               u8 addr_type, u8 *dev_class, s8 rssi, u8 *eir);
  int mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *name);
- int mgmt_inquiry_failed(struct hci_dev *hdev, u8 status);
+ int mgmt_start_discovery_failed(struct hci_dev *hdev, u8 status);
+ int mgmt_stop_discovery_failed(struct hci_dev *hdev, u8 status);
  int mgmt_discovering(struct hci_dev *hdev, u8 discovering);
  int mgmt_device_blocked(struct hci_dev *hdev, bdaddr_t *bdaddr);
  int mgmt_device_unblocked(struct hci_dev *hdev, bdaddr_t *bdaddr);
index e2e785c746306a6f4b79f10353b8f3fe7aa5d986,b85e3906b3a8b8730c77ec04b85dc9b1ae5d2862..f73704321a77d0eaf74b3570da554855405a0358
@@@ -27,7 -27,6 +27,7 @@@
  /* Bluetooth L2CAP sockets. */
  
  #include <linux/security.h>
 +#include <linux/export.h>
  
  #include <net/bluetooth/bluetooth.h>
  #include <net/bluetooth/hci_core.h>
@@@ -626,8 -625,13 +626,13 @@@ static int l2cap_sock_setsockopt(struc
  
                chan->sec_level = sec.level;
  
+               if (!chan->conn)
+                       break;
                conn = chan->conn;
-               if (conn && chan->scid == L2CAP_CID_LE_DATA) {
+               /*change security for LE channels */
+               if (chan->scid == L2CAP_CID_LE_DATA) {
                        if (!conn->hcon->out) {
                                err = -EINVAL;
                                break;
  
                        if (smp_conn_security(conn, sec.level))
                                break;
-                       err = 0;
                        sk->sk_state = BT_CONFIG;
+               /* or for ACL link, under defer_setup time */
+               } else if (sk->sk_state == BT_CONNECT2 &&
+                                       bt_sk(sk)->defer_setup) {
+                       err = l2cap_chan_check_security(chan);
+               } else {
+                       err = -EINVAL;
                }
                break;
  
diff --combined net/bluetooth/mgmt.c
index 94739d3c4f59ccabb5ed378c6795150a85c60c79,7a23f211d60235612a6a5e93aa5e3e70cf771e48..1ce549bae241809104194b8ed545a81c35821586
@@@ -22,8 -22,8 +22,9 @@@
  
  /* Bluetooth HCI Management interface */
  
+ #include <linux/kernel.h>
  #include <linux/uaccess.h>
 +#include <linux/module.h>
  #include <asm/unaligned.h>
  
  #include <net/bluetooth/bluetooth.h>
@@@ -44,6 -44,79 +45,79 @@@ struct pending_cmd 
        void *user_data;
  };
  
+ /* HCI to MGMT error code conversion table */
+ static u8 mgmt_status_table[] = {
+       MGMT_STATUS_SUCCESS,
+       MGMT_STATUS_UNKNOWN_COMMAND,    /* Unknown Command */
+       MGMT_STATUS_NOT_CONNECTED,      /* No Connection */
+       MGMT_STATUS_FAILED,             /* Hardware Failure */
+       MGMT_STATUS_CONNECT_FAILED,     /* Page Timeout */
+       MGMT_STATUS_AUTH_FAILED,        /* Authentication Failed */
+       MGMT_STATUS_NOT_PAIRED,         /* PIN or Key Missing */
+       MGMT_STATUS_NO_RESOURCES,       /* Memory Full */
+       MGMT_STATUS_TIMEOUT,            /* Connection Timeout */
+       MGMT_STATUS_NO_RESOURCES,       /* Max Number of Connections */
+       MGMT_STATUS_NO_RESOURCES,       /* Max Number of SCO Connections */
+       MGMT_STATUS_ALREADY_CONNECTED,  /* ACL Connection Exists */
+       MGMT_STATUS_BUSY,               /* Command Disallowed */
+       MGMT_STATUS_NO_RESOURCES,       /* Rejected Limited Resources */
+       MGMT_STATUS_REJECTED,           /* Rejected Security */
+       MGMT_STATUS_REJECTED,           /* Rejected Personal */
+       MGMT_STATUS_TIMEOUT,            /* Host Timeout */
+       MGMT_STATUS_NOT_SUPPORTED,      /* Unsupported Feature */
+       MGMT_STATUS_INVALID_PARAMS,     /* Invalid Parameters */
+       MGMT_STATUS_DISCONNECTED,       /* OE User Ended Connection */
+       MGMT_STATUS_NO_RESOURCES,       /* OE Low Resources */
+       MGMT_STATUS_DISCONNECTED,       /* OE Power Off */
+       MGMT_STATUS_DISCONNECTED,       /* Connection Terminated */
+       MGMT_STATUS_BUSY,               /* Repeated Attempts */
+       MGMT_STATUS_REJECTED,           /* Pairing Not Allowed */
+       MGMT_STATUS_FAILED,             /* Unknown LMP PDU */
+       MGMT_STATUS_NOT_SUPPORTED,      /* Unsupported Remote Feature */
+       MGMT_STATUS_REJECTED,           /* SCO Offset Rejected */
+       MGMT_STATUS_REJECTED,           /* SCO Interval Rejected */
+       MGMT_STATUS_REJECTED,           /* Air Mode Rejected */
+       MGMT_STATUS_INVALID_PARAMS,     /* Invalid LMP Parameters */
+       MGMT_STATUS_FAILED,             /* Unspecified Error */
+       MGMT_STATUS_NOT_SUPPORTED,      /* Unsupported LMP Parameter Value */
+       MGMT_STATUS_FAILED,             /* Role Change Not Allowed */
+       MGMT_STATUS_TIMEOUT,            /* LMP Response Timeout */
+       MGMT_STATUS_FAILED,             /* LMP Error Transaction Collision */
+       MGMT_STATUS_FAILED,             /* LMP PDU Not Allowed */
+       MGMT_STATUS_REJECTED,           /* Encryption Mode Not Accepted */
+       MGMT_STATUS_FAILED,             /* Unit Link Key Used */
+       MGMT_STATUS_NOT_SUPPORTED,      /* QoS Not Supported */
+       MGMT_STATUS_TIMEOUT,            /* Instant Passed */
+       MGMT_STATUS_NOT_SUPPORTED,      /* Pairing Not Supported */
+       MGMT_STATUS_FAILED,             /* Transaction Collision */
+       MGMT_STATUS_INVALID_PARAMS,     /* Unacceptable Parameter */
+       MGMT_STATUS_REJECTED,           /* QoS Rejected */
+       MGMT_STATUS_NOT_SUPPORTED,      /* Classification Not Supported */
+       MGMT_STATUS_REJECTED,           /* Insufficient Security */
+       MGMT_STATUS_INVALID_PARAMS,     /* Parameter Out Of Range */
+       MGMT_STATUS_BUSY,               /* Role Switch Pending */
+       MGMT_STATUS_FAILED,             /* Slot Violation */
+       MGMT_STATUS_FAILED,             /* Role Switch Failed */
+       MGMT_STATUS_INVALID_PARAMS,     /* EIR Too Large */
+       MGMT_STATUS_NOT_SUPPORTED,      /* Simple Pairing Not Supported */
+       MGMT_STATUS_BUSY,               /* Host Busy Pairing */
+       MGMT_STATUS_REJECTED,           /* Rejected, No Suitable Channel */
+       MGMT_STATUS_BUSY,               /* Controller Busy */
+       MGMT_STATUS_INVALID_PARAMS,     /* Unsuitable Connection Interval */
+       MGMT_STATUS_TIMEOUT,            /* Directed Advertising Timeout */
+       MGMT_STATUS_AUTH_FAILED,        /* Terminated Due to MIC Failure */
+       MGMT_STATUS_CONNECT_FAILED,     /* Connection Establishment Failed */
+       MGMT_STATUS_CONNECT_FAILED,     /* MAC Connection Failed */
+ };
+ static u8 mgmt_status(u8 hci_status)
+ {
+       if (hci_status < ARRAY_SIZE(mgmt_status_table))
+               return mgmt_status_table[hci_status];
+       return MGMT_STATUS_FAILED;
+ }
  static int cmd_status(struct sock *sk, u16 index, u16 cmd, u8 status)
  {
        struct sk_buff *skb;
@@@ -178,7 -251,8 +252,8 @@@ static int read_controller_info(struct 
  
        hdev = hci_dev_get(index);
        if (!hdev)
-               return cmd_status(sk, index, MGMT_OP_READ_INFO, ENODEV);
+               return cmd_status(sk, index, MGMT_OP_READ_INFO,
+                                               MGMT_STATUS_INVALID_PARAMS);
  
        if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->flags))
                cancel_delayed_work_sync(&hdev->power_off);
@@@ -291,6 -365,15 +366,15 @@@ static void mgmt_pending_remove(struct 
        mgmt_pending_free(cmd);
  }
  
+ static int send_mode_rsp(struct sock *sk, u16 opcode, u16 index, u8 val)
+ {
+       struct mgmt_mode rp;
+       rp.val = val;
+       return cmd_complete(sk, index, opcode, &rp, sizeof(rp));
+ }
  static int set_powered(struct sock *sk, u16 index, unsigned char *data, u16 len)
  {
        struct mgmt_mode *cp;
        BT_DBG("request for hci%u", index);
  
        if (len != sizeof(*cp))
-               return cmd_status(sk, index, MGMT_OP_SET_POWERED, EINVAL);
+               return cmd_status(sk, index, MGMT_OP_SET_POWERED,
+                                               MGMT_STATUS_INVALID_PARAMS);
  
        hdev = hci_dev_get(index);
        if (!hdev)
-               return cmd_status(sk, index, MGMT_OP_SET_POWERED, ENODEV);
+               return cmd_status(sk, index, MGMT_OP_SET_POWERED,
+                                               MGMT_STATUS_INVALID_PARAMS);
  
        hci_dev_lock_bh(hdev);
  
        up = test_bit(HCI_UP, &hdev->flags);
        if ((cp->val && up) || (!cp->val && !up)) {
-               err = cmd_status(sk, index, MGMT_OP_SET_POWERED, EALREADY);
+               err = send_mode_rsp(sk, index, MGMT_OP_SET_POWERED, cp->val);
                goto failed;
        }
  
        if (mgmt_pending_find(MGMT_OP_SET_POWERED, hdev)) {
-               err = cmd_status(sk, index, MGMT_OP_SET_POWERED, EBUSY);
+               err = cmd_status(sk, index, MGMT_OP_SET_POWERED,
+                                                       MGMT_STATUS_BUSY);
                goto failed;
        }
  
@@@ -355,28 -441,33 +442,33 @@@ static int set_discoverable(struct soc
        BT_DBG("request for hci%u", index);
  
        if (len != sizeof(*cp))
-               return cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE, EINVAL);
+               return cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE,
+                                               MGMT_STATUS_INVALID_PARAMS);
  
        hdev = hci_dev_get(index);
        if (!hdev)
-               return cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE, ENODEV);
+               return cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE,
+                                               MGMT_STATUS_INVALID_PARAMS);
  
        hci_dev_lock_bh(hdev);
  
        if (!test_bit(HCI_UP, &hdev->flags)) {
-               err = cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE, ENETDOWN);
+               err = cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE,
+                                               MGMT_STATUS_NOT_POWERED);
                goto failed;
        }
  
        if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, hdev) ||
                        mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, hdev)) {
-               err = cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE, EBUSY);
+               err = cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE,
+                                                       MGMT_STATUS_BUSY);
                goto failed;
        }
  
        if (cp->val == test_bit(HCI_ISCAN, &hdev->flags) &&
                                        test_bit(HCI_PSCAN, &hdev->flags)) {
-               err = cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE, EALREADY);
+               err = send_mode_rsp(sk, index, MGMT_OP_SET_DISCOVERABLE,
+                                                               cp->val);
                goto failed;
        }
  
@@@ -421,27 -512,32 +513,32 @@@ static int set_connectable(struct sock 
        BT_DBG("request for hci%u", index);
  
        if (len != sizeof(*cp))
-               return cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE, EINVAL);
+               return cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE,
+                                               MGMT_STATUS_INVALID_PARAMS);
  
        hdev = hci_dev_get(index);
        if (!hdev)
-               return cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE, ENODEV);
+               return cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE,
+                                               MGMT_STATUS_INVALID_PARAMS);
  
        hci_dev_lock_bh(hdev);
  
        if (!test_bit(HCI_UP, &hdev->flags)) {
-               err = cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE, ENETDOWN);
+               err = cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE,
+                                               MGMT_STATUS_NOT_POWERED);
                goto failed;
        }
  
        if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, hdev) ||
                        mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, hdev)) {
-               err = cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE, EBUSY);
+               err = cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE,
+                                                       MGMT_STATUS_BUSY);
                goto failed;
        }
  
        if (cp->val == test_bit(HCI_PSCAN, &hdev->flags)) {
-               err = cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE, EALREADY);
+               err = send_mode_rsp(sk, index, MGMT_OP_SET_CONNECTABLE,
+                                                               cp->val);
                goto failed;
        }
  
@@@ -496,15 -592,6 +593,6 @@@ static int mgmt_event(u16 event, struc
        return 0;
  }
  
- static int send_mode_rsp(struct sock *sk, u16 opcode, u16 index, u8 val)
- {
-       struct mgmt_mode rp;
-       rp.val = val;
-       return cmd_complete(sk, index, opcode, &rp, sizeof(rp));
- }
  static int set_pairable(struct sock *sk, u16 index, unsigned char *data,
                                                                        u16 len)
  {
        BT_DBG("request for hci%u", index);
  
        if (len != sizeof(*cp))
-               return cmd_status(sk, index, MGMT_OP_SET_PAIRABLE, EINVAL);
+               return cmd_status(sk, index, MGMT_OP_SET_PAIRABLE,
+                                               MGMT_STATUS_INVALID_PARAMS);
  
        hdev = hci_dev_get(index);
        if (!hdev)
-               return cmd_status(sk, index, MGMT_OP_SET_PAIRABLE, ENODEV);
+               return cmd_status(sk, index, MGMT_OP_SET_PAIRABLE,
+                                               MGMT_STATUS_INVALID_PARAMS);
  
        hci_dev_lock_bh(hdev);
  
@@@ -730,11 -819,13 +820,13 @@@ static int add_uuid(struct sock *sk, u1
        BT_DBG("request for hci%u", index);
  
        if (len != sizeof(*cp))
-               return cmd_status(sk, index, MGMT_OP_ADD_UUID, EINVAL);
+               return cmd_status(sk, index, MGMT_OP_ADD_UUID,
+                                               MGMT_STATUS_INVALID_PARAMS);
  
        hdev = hci_dev_get(index);
        if (!hdev)
-               return cmd_status(sk, index, MGMT_OP_ADD_UUID, ENODEV);
+               return cmd_status(sk, index, MGMT_OP_ADD_UUID,
+                                               MGMT_STATUS_INVALID_PARAMS);
  
        hci_dev_lock_bh(hdev);
  
@@@ -779,11 -870,13 +871,13 @@@ static int remove_uuid(struct sock *sk
        BT_DBG("request for hci%u", index);
  
        if (len != sizeof(*cp))
-               return cmd_status(sk, index, MGMT_OP_REMOVE_UUID, EINVAL);
+               return cmd_status(sk, index, MGMT_OP_REMOVE_UUID,
+                                               MGMT_STATUS_INVALID_PARAMS);
  
        hdev = hci_dev_get(index);
        if (!hdev)
-               return cmd_status(sk, index, MGMT_OP_REMOVE_UUID, ENODEV);
+               return cmd_status(sk, index, MGMT_OP_REMOVE_UUID,
+                                               MGMT_STATUS_INVALID_PARAMS);
  
        hci_dev_lock_bh(hdev);
  
        }
  
        if (found == 0) {
-               err = cmd_status(sk, index, MGMT_OP_REMOVE_UUID, ENOENT);
+               err = cmd_status(sk, index, MGMT_OP_REMOVE_UUID,
+                                               MGMT_STATUS_INVALID_PARAMS);
                goto unlock;
        }
  
@@@ -838,11 -932,13 +933,13 @@@ static int set_dev_class(struct sock *s
        BT_DBG("request for hci%u", index);
  
        if (len != sizeof(*cp))
-               return cmd_status(sk, index, MGMT_OP_SET_DEV_CLASS, EINVAL);
+               return cmd_status(sk, index, MGMT_OP_SET_DEV_CLASS,
+                                               MGMT_STATUS_INVALID_PARAMS);
  
        hdev = hci_dev_get(index);
        if (!hdev)
-               return cmd_status(sk, index, MGMT_OP_SET_DEV_CLASS, ENODEV);
+               return cmd_status(sk, index, MGMT_OP_SET_DEV_CLASS,
+                                               MGMT_STATUS_INVALID_PARAMS);
  
        hci_dev_lock_bh(hdev);
  
@@@ -870,11 -966,13 +967,13 @@@ static int set_service_cache(struct soc
        cp = (void *) data;
  
        if (len != sizeof(*cp))
-               return cmd_status(sk, index, MGMT_OP_SET_SERVICE_CACHE, EINVAL);
+               return cmd_status(sk, index, MGMT_OP_SET_SERVICE_CACHE,
+                                               MGMT_STATUS_INVALID_PARAMS);
  
        hdev = hci_dev_get(index);
        if (!hdev)
-               return cmd_status(sk, index, MGMT_OP_SET_SERVICE_CACHE, ENODEV);
+               return cmd_status(sk, index, MGMT_OP_SET_SERVICE_CACHE,
+                                               MGMT_STATUS_INVALID_PARAMS);
  
        hci_dev_lock_bh(hdev);
  
@@@ -914,7 -1012,8 +1013,8 @@@ static int load_link_keys(struct sock *
        cp = (void *) data;
  
        if (len < sizeof(*cp))
-               return cmd_status(sk, index, MGMT_OP_LOAD_LINK_KEYS, EINVAL);
+               return cmd_status(sk, index, MGMT_OP_LOAD_LINK_KEYS,
+                                               MGMT_STATUS_INVALID_PARAMS);
  
        key_count = get_unaligned_le16(&cp->key_count);
  
        if (expected_len != len) {
                BT_ERR("load_link_keys: expected %u bytes, got %u bytes",
                                                        len, expected_len);
-               return cmd_status(sk, index, MGMT_OP_LOAD_LINK_KEYS, EINVAL);
+               return cmd_status(sk, index, MGMT_OP_LOAD_LINK_KEYS,
+                                               MGMT_STATUS_INVALID_PARAMS);
        }
  
        hdev = hci_dev_get(index);
        if (!hdev)
-               return cmd_status(sk, index, MGMT_OP_LOAD_LINK_KEYS, ENODEV);
+               return cmd_status(sk, index, MGMT_OP_LOAD_LINK_KEYS,
+                                               MGMT_STATUS_INVALID_PARAMS);
  
        BT_DBG("hci%u debug_keys %u key_count %u", index, cp->debug_keys,
                                                                key_count);
                                                                key->pin_len);
        }
  
+       cmd_complete(sk, index, MGMT_OP_LOAD_LINK_KEYS, NULL, 0);
        hci_dev_unlock_bh(hdev);
        hci_dev_put(hdev);
  
@@@ -962,41 -1065,64 +1066,64 @@@ static int remove_keys(struct sock *sk
  {
        struct hci_dev *hdev;
        struct mgmt_cp_remove_keys *cp;
+       struct mgmt_rp_remove_keys rp;
+       struct hci_cp_disconnect dc;
+       struct pending_cmd *cmd;
        struct hci_conn *conn;
        int err;
  
        cp = (void *) data;
  
        if (len != sizeof(*cp))
-               return cmd_status(sk, index, MGMT_OP_REMOVE_KEYS, EINVAL);
+               return cmd_status(sk, index, MGMT_OP_REMOVE_KEYS,
+                                               MGMT_STATUS_INVALID_PARAMS);
  
        hdev = hci_dev_get(index);
        if (!hdev)
-               return cmd_status(sk, index, MGMT_OP_REMOVE_KEYS, ENODEV);
+               return cmd_status(sk, index, MGMT_OP_REMOVE_KEYS,
+                                               MGMT_STATUS_INVALID_PARAMS);
  
        hci_dev_lock_bh(hdev);
  
+       memset(&rp, 0, sizeof(rp));
+       bacpy(&rp.bdaddr, &cp->bdaddr);
+       rp.status = MGMT_STATUS_FAILED;
        err = hci_remove_link_key(hdev, &cp->bdaddr);
        if (err < 0) {
-               err = cmd_status(sk, index, MGMT_OP_REMOVE_KEYS, -err);
+               rp.status = MGMT_STATUS_NOT_PAIRED;
                goto unlock;
        }
  
-       err = 0;
-       if (!test_bit(HCI_UP, &hdev->flags) || !cp->disconnect)
+       if (!test_bit(HCI_UP, &hdev->flags) || !cp->disconnect) {
+               err = cmd_complete(sk, index, MGMT_OP_REMOVE_KEYS, &rp,
+                                                               sizeof(rp));
                goto unlock;
+       }
  
        conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr);
-       if (conn) {
-               struct hci_cp_disconnect dc;
+       if (!conn) {
+               err = cmd_complete(sk, index, MGMT_OP_REMOVE_KEYS, &rp,
+                                                               sizeof(rp));
+               goto unlock;
+       }
  
-               put_unaligned_le16(conn->handle, &dc.handle);
-               dc.reason = 0x13; /* Remote User Terminated Connection */
-               err = hci_send_cmd(hdev, HCI_OP_DISCONNECT, sizeof(dc), &dc);
+       cmd = mgmt_pending_add(sk, MGMT_OP_REMOVE_KEYS, hdev, cp, sizeof(*cp));
+       if (!cmd) {
+               err = -ENOMEM;
+               goto unlock;
        }
  
+       put_unaligned_le16(conn->handle, &dc.handle);
+       dc.reason = 0x13; /* Remote User Terminated Connection */
+       err = hci_send_cmd(hdev, HCI_OP_DISCONNECT, sizeof(dc), &dc);
+       if (err < 0)
+               mgmt_pending_remove(cmd);
  unlock:
+       if (err < 0)
+               err = cmd_complete(sk, index, MGMT_OP_REMOVE_KEYS, &rp,
+                                                               sizeof(rp));
        hci_dev_unlock_bh(hdev);
        hci_dev_put(hdev);
  
@@@ -1017,21 -1143,25 +1144,25 @@@ static int disconnect(struct sock *sk, 
        cp = (void *) data;
  
        if (len != sizeof(*cp))
-               return cmd_status(sk, index, MGMT_OP_DISCONNECT, EINVAL);
+               return cmd_status(sk, index, MGMT_OP_DISCONNECT,
+                                               MGMT_STATUS_INVALID_PARAMS);
  
        hdev = hci_dev_get(index);
        if (!hdev)
-               return cmd_status(sk, index, MGMT_OP_DISCONNECT, ENODEV);
+               return cmd_status(sk, index, MGMT_OP_DISCONNECT,
+                                               MGMT_STATUS_INVALID_PARAMS);
  
        hci_dev_lock_bh(hdev);
  
        if (!test_bit(HCI_UP, &hdev->flags)) {
-               err = cmd_status(sk, index, MGMT_OP_DISCONNECT, ENETDOWN);
+               err = cmd_status(sk, index, MGMT_OP_DISCONNECT,
+                                               MGMT_STATUS_NOT_POWERED);
                goto failed;
        }
  
        if (mgmt_pending_find(MGMT_OP_DISCONNECT, hdev)) {
-               err = cmd_status(sk, index, MGMT_OP_DISCONNECT, EBUSY);
+               err = cmd_status(sk, index, MGMT_OP_DISCONNECT,
+                                                       MGMT_STATUS_BUSY);
                goto failed;
        }
  
                conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->bdaddr);
  
        if (!conn) {
-               err = cmd_status(sk, index, MGMT_OP_DISCONNECT, ENOTCONN);
+               err = cmd_status(sk, index, MGMT_OP_DISCONNECT,
+                                               MGMT_STATUS_NOT_CONNECTED);
                goto failed;
        }
  
@@@ -1064,11 -1195,18 +1196,18 @@@ failed
        return err;
  }
  
- static u8 link_to_mgmt(u8 link_type)
+ static u8 link_to_mgmt(u8 link_type, u8 addr_type)
  {
        switch (link_type) {
        case LE_LINK:
-               return MGMT_ADDR_LE;
+               switch (addr_type) {
+               case ADDR_LE_DEV_PUBLIC:
+                       return MGMT_ADDR_LE_PUBLIC;
+               case ADDR_LE_DEV_RANDOM:
+                       return MGMT_ADDR_LE_RANDOM;
+               default:
+                       return MGMT_ADDR_INVALID;
+               }
        case ACL_LINK:
                return MGMT_ADDR_BREDR;
        default:
@@@ -1090,7 -1228,8 +1229,8 @@@ static int get_connections(struct sock 
  
        hdev = hci_dev_get(index);
        if (!hdev)
-               return cmd_status(sk, index, MGMT_OP_GET_CONNECTIONS, ENODEV);
+               return cmd_status(sk, index, MGMT_OP_GET_CONNECTIONS,
+                                               MGMT_STATUS_INVALID_PARAMS);
  
        hci_dev_lock_bh(hdev);
  
        i = 0;
        list_for_each_entry(c, &hdev->conn_hash.list, list) {
                bacpy(&rp->addr[i].bdaddr, &c->dst);
-               rp->addr[i].type = link_to_mgmt(c->type);
+               rp->addr[i].type = link_to_mgmt(c->type, c->dst_type);
                if (rp->addr[i].type == MGMT_ADDR_INVALID)
                        continue;
                i++;
@@@ -1164,22 -1303,26 +1304,26 @@@ static int pin_code_reply(struct sock *
        cp = (void *) data;
  
        if (len != sizeof(*cp))
-               return cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY, EINVAL);
+               return cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY,
+                                               MGMT_STATUS_INVALID_PARAMS);
  
        hdev = hci_dev_get(index);
        if (!hdev)
-               return cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY, ENODEV);
+               return cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY,
+                                               MGMT_STATUS_INVALID_PARAMS);
  
        hci_dev_lock_bh(hdev);
  
        if (!test_bit(HCI_UP, &hdev->flags)) {
-               err = cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY, ENETDOWN);
+               err = cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY,
+                                               MGMT_STATUS_NOT_POWERED);
                goto failed;
        }
  
        conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr);
        if (!conn) {
-               err = cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY, ENOTCONN);
+               err = cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY,
+                                               MGMT_STATUS_NOT_CONNECTED);
                goto failed;
        }
  
                err = send_pin_code_neg_reply(sk, index, hdev, &ncp);
                if (err >= 0)
                        err = cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY,
-                                                               EINVAL);
+                                               MGMT_STATUS_INVALID_PARAMS);
  
                goto failed;
        }
@@@ -1230,18 -1373,18 +1374,18 @@@ static int pin_code_neg_reply(struct so
  
        if (len != sizeof(*cp))
                return cmd_status(sk, index, MGMT_OP_PIN_CODE_NEG_REPLY,
-                                                                       EINVAL);
+                                               MGMT_STATUS_INVALID_PARAMS);
  
        hdev = hci_dev_get(index);
        if (!hdev)
                return cmd_status(sk, index, MGMT_OP_PIN_CODE_NEG_REPLY,
-                                                                       ENODEV);
+                                               MGMT_STATUS_INVALID_PARAMS);
  
        hci_dev_lock_bh(hdev);
  
        if (!test_bit(HCI_UP, &hdev->flags)) {
                err = cmd_status(sk, index, MGMT_OP_PIN_CODE_NEG_REPLY,
-                                                               ENETDOWN);
+                                               MGMT_STATUS_NOT_POWERED);
                goto failed;
        }
  
@@@ -1265,11 -1408,13 +1409,13 @@@ static int set_io_capability(struct soc
        cp = (void *) data;
  
        if (len != sizeof(*cp))
-               return cmd_status(sk, index, MGMT_OP_SET_IO_CAPABILITY, EINVAL);
+               return cmd_status(sk, index, MGMT_OP_SET_IO_CAPABILITY,
+                                               MGMT_STATUS_INVALID_PARAMS);
  
        hdev = hci_dev_get(index);
        if (!hdev)
-               return cmd_status(sk, index, MGMT_OP_SET_IO_CAPABILITY, ENODEV);
+               return cmd_status(sk, index, MGMT_OP_SET_IO_CAPABILITY,
+                                               MGMT_STATUS_INVALID_PARAMS);
  
        hci_dev_lock_bh(hdev);
  
@@@ -1307,7 -1452,8 +1453,8 @@@ static void pairing_complete(struct pen
        struct mgmt_rp_pair_device rp;
        struct hci_conn *conn = cmd->user_data;
  
-       bacpy(&rp.bdaddr, &conn->dst);
+       bacpy(&rp.addr.bdaddr, &conn->dst);
+       rp.addr.type = link_to_mgmt(conn->type, conn->dst_type);
        rp.status = status;
  
        cmd_complete(cmd->sk, cmd->index, MGMT_OP_PAIR_DEVICE, &rp, sizeof(rp));
  static void pairing_complete_cb(struct hci_conn *conn, u8 status)
  {
        struct pending_cmd *cmd;
-       struct hci_dev *hdev = conn->hdev;
  
        BT_DBG("status %u", status);
  
-       hci_dev_lock_bh(hdev);
        cmd = find_pairing(conn);
        if (!cmd)
                BT_DBG("Unable to find a pending command");
        else
                pairing_complete(cmd, status);
-       hci_dev_unlock_bh(hdev);
  }
  
  static int pair_device(struct sock *sk, u16 index, unsigned char *data, u16 len)
  {
        struct hci_dev *hdev;
        struct mgmt_cp_pair_device *cp;
+       struct mgmt_rp_pair_device rp;
        struct pending_cmd *cmd;
-       struct adv_entry *entry;
        u8 sec_level, auth_type;
        struct hci_conn *conn;
        int err;
        cp = (void *) data;
  
        if (len != sizeof(*cp))
-               return cmd_status(sk, index, MGMT_OP_PAIR_DEVICE, EINVAL);
+               return cmd_status(sk, index, MGMT_OP_PAIR_DEVICE,
+                                               MGMT_STATUS_INVALID_PARAMS);
  
        hdev = hci_dev_get(index);
        if (!hdev)
-               return cmd_status(sk, index, MGMT_OP_PAIR_DEVICE, ENODEV);
+               return cmd_status(sk, index, MGMT_OP_PAIR_DEVICE,
+                                               MGMT_STATUS_INVALID_PARAMS);
  
        hci_dev_lock_bh(hdev);
  
        else
                auth_type = HCI_AT_DEDICATED_BONDING_MITM;
  
-       entry = hci_find_adv_entry(hdev, &cp->bdaddr);
-       if (entry)
-               conn = hci_connect(hdev, LE_LINK, &cp->bdaddr, sec_level,
+       if (cp->addr.type == MGMT_ADDR_BREDR)
+               conn = hci_connect(hdev, ACL_LINK, &cp->addr.bdaddr, sec_level,
                                                                auth_type);
        else
-               conn = hci_connect(hdev, ACL_LINK, &cp->bdaddr, sec_level,
+               conn = hci_connect(hdev, LE_LINK, &cp->addr.bdaddr, sec_level,
                                                                auth_type);
  
+       memset(&rp, 0, sizeof(rp));
+       bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
+       rp.addr.type = cp->addr.type;
        if (IS_ERR(conn)) {
-               err = PTR_ERR(conn);
+               rp.status = -PTR_ERR(conn);
+               err = cmd_complete(sk, index, MGMT_OP_PAIR_DEVICE,
+                                                       &rp, sizeof(rp));
                goto unlock;
        }
  
        if (conn->connect_cfm_cb) {
                hci_conn_put(conn);
-               err = cmd_status(sk, index, MGMT_OP_PAIR_DEVICE, EBUSY);
+               rp.status = EBUSY;
+               err = cmd_complete(sk, index, MGMT_OP_PAIR_DEVICE,
+                                                       &rp, sizeof(rp));
                goto unlock;
        }
  
        }
  
        /* For LE, just connecting isn't a proof that the pairing finished */
-       if (!entry)
+       if (cp->addr.type == MGMT_ADDR_BREDR)
                conn->connect_cfm_cb = pairing_complete_cb;
  
        conn->security_cfm_cb = pairing_complete_cb;
@@@ -1417,56 -1567,138 +1568,138 @@@ 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, EINVAL);
        hdev = hci_dev_get(index);
        if (!hdev)
-               return cmd_status(sk, index, mgmt_op, ENODEV);
+               return cmd_status(sk, index, mgmt_op,
+                                               MGMT_STATUS_INVALID_PARAMS);
  
        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;
        }
  
-       cmd = mgmt_pending_add(sk, mgmt_op, hdev, data, len);
+       /*
+        * 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, 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)
  {
        BT_DBG("");
  
        if (len != sizeof(*mgmt_cp))
-               return cmd_status(sk, index, MGMT_OP_SET_LOCAL_NAME, EINVAL);
+               return cmd_status(sk, index, MGMT_OP_SET_LOCAL_NAME,
+                                               MGMT_STATUS_INVALID_PARAMS);
  
        hdev = hci_dev_get(index);
        if (!hdev)
-               return cmd_status(sk, index, MGMT_OP_SET_LOCAL_NAME, ENODEV);
+               return cmd_status(sk, index, MGMT_OP_SET_LOCAL_NAME,
+                                               MGMT_STATUS_INVALID_PARAMS);
  
        hci_dev_lock_bh(hdev);
  
@@@ -1517,24 -1751,25 +1752,25 @@@ static int read_local_oob_data(struct s
        hdev = hci_dev_get(index);
        if (!hdev)
                return cmd_status(sk, index, MGMT_OP_READ_LOCAL_OOB_DATA,
-                                                                       ENODEV);
+                                               MGMT_STATUS_INVALID_PARAMS);
  
        hci_dev_lock_bh(hdev);
  
        if (!test_bit(HCI_UP, &hdev->flags)) {
                err = cmd_status(sk, index, MGMT_OP_READ_LOCAL_OOB_DATA,
-                                                               ENETDOWN);
+                                               MGMT_STATUS_NOT_POWERED);
                goto unlock;
        }
  
        if (!(hdev->features[6] & LMP_SIMPLE_PAIR)) {
                err = cmd_status(sk, index, MGMT_OP_READ_LOCAL_OOB_DATA,
-                                                               EOPNOTSUPP);
+                                               MGMT_STATUS_NOT_SUPPORTED);
                goto unlock;
        }
  
        if (mgmt_pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, hdev)) {
-               err = cmd_status(sk, index, MGMT_OP_READ_LOCAL_OOB_DATA, EBUSY);
+               err = cmd_status(sk, index, MGMT_OP_READ_LOCAL_OOB_DATA,
+                                                       MGMT_STATUS_BUSY);
                goto unlock;
        }
  
@@@ -1566,19 -1801,20 +1802,20 @@@ static int add_remote_oob_data(struct s
  
        if (len != sizeof(*cp))
                return cmd_status(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA,
-                                                                       EINVAL);
+                                               MGMT_STATUS_INVALID_PARAMS);
  
        hdev = hci_dev_get(index);
        if (!hdev)
                return cmd_status(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA,
-                                                                       ENODEV);
+                                               MGMT_STATUS_INVALID_PARAMS);
  
        hci_dev_lock_bh(hdev);
  
        err = hci_add_remote_oob_data(hdev, &cp->bdaddr, cp->hash,
                                                                cp->randomizer);
        if (err < 0)
-               err = cmd_status(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA, -err);
+               err = cmd_status(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA,
+                                                       MGMT_STATUS_FAILED);
        else
                err = cmd_complete(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA, NULL,
                                                                        0);
@@@ -1600,19 -1836,19 +1837,19 @@@ static int remove_remote_oob_data(struc
  
        if (len != sizeof(*cp))
                return cmd_status(sk, index, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
-                                                                       EINVAL);
+                                               MGMT_STATUS_INVALID_PARAMS);
  
        hdev = hci_dev_get(index);
        if (!hdev)
                return cmd_status(sk, index, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
-                                                                       ENODEV);
+                                               MGMT_STATUS_INVALID_PARAMS);
  
        hci_dev_lock_bh(hdev);
  
        err = hci_remove_remote_oob_data(hdev, &cp->bdaddr);
        if (err < 0)
                err = cmd_status(sk, index, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
-                                                                       -err);
+                                               MGMT_STATUS_INVALID_PARAMS);
        else
                err = cmd_complete(sk, index, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
                                                                NULL, 0);
        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, ENODEV);
+               return cmd_status(sk, index, MGMT_OP_START_DISCOVERY,
+                                               MGMT_STATUS_INVALID_PARAMS);
  
        hci_dev_lock_bh(hdev);
  
        if (!test_bit(HCI_UP, &hdev->flags)) {
-               err = cmd_status(sk, index, MGMT_OP_START_DISCOVERY, ENETDOWN);
+               err = cmd_status(sk, index, MGMT_OP_START_DISCOVERY,
+                                               MGMT_STATUS_NOT_POWERED);
                goto failed;
        }
  
@@@ -1669,7 -1913,8 +1914,8 @@@ static int stop_discovery(struct sock *
  
        hdev = hci_dev_get(index);
        if (!hdev)
-               return cmd_status(sk, index, MGMT_OP_STOP_DISCOVERY, ENODEV);
+               return cmd_status(sk, index, MGMT_OP_STOP_DISCOVERY,
+                                               MGMT_STATUS_INVALID_PARAMS);
  
        hci_dev_lock_bh(hdev);
  
@@@ -1701,18 -1946,19 +1947,19 @@@ static int block_device(struct sock *sk
  
        if (len != sizeof(*cp))
                return cmd_status(sk, index, MGMT_OP_BLOCK_DEVICE,
-                                                       EINVAL);
+                                               MGMT_STATUS_INVALID_PARAMS);
  
        hdev = hci_dev_get(index);
        if (!hdev)
                return cmd_status(sk, index, MGMT_OP_BLOCK_DEVICE,
-                                                       ENODEV);
+                                               MGMT_STATUS_INVALID_PARAMS);
  
        hci_dev_lock_bh(hdev);
  
        err = hci_blacklist_add(hdev, &cp->bdaddr);
        if (err < 0)
-               err = cmd_status(sk, index, MGMT_OP_BLOCK_DEVICE, -err);
+               err = cmd_status(sk, index, MGMT_OP_BLOCK_DEVICE,
+                                                       MGMT_STATUS_FAILED);
        else
                err = cmd_complete(sk, index, MGMT_OP_BLOCK_DEVICE,
                                                        NULL, 0);
@@@ -1734,19 -1980,20 +1981,20 @@@ static int unblock_device(struct sock *
  
        if (len != sizeof(*cp))
                return cmd_status(sk, index, MGMT_OP_UNBLOCK_DEVICE,
-                                                               EINVAL);
+                                               MGMT_STATUS_INVALID_PARAMS);
  
        hdev = hci_dev_get(index);
        if (!hdev)
                return cmd_status(sk, index, MGMT_OP_UNBLOCK_DEVICE,
-                                                               ENODEV);
+                                               MGMT_STATUS_INVALID_PARAMS);
  
        hci_dev_lock_bh(hdev);
  
        err = hci_blacklist_del(hdev, &cp->bdaddr);
  
        if (err < 0)
-               err = cmd_status(sk, index, MGMT_OP_UNBLOCK_DEVICE, -err);
+               err = cmd_status(sk, index, MGMT_OP_UNBLOCK_DEVICE,
+                                               MGMT_STATUS_INVALID_PARAMS);
        else
                err = cmd_complete(sk, index, MGMT_OP_UNBLOCK_DEVICE,
                                                                NULL, 0);
@@@ -1770,12 -2017,12 +2018,12 @@@ static int set_fast_connectable(struct 
  
        if (len != sizeof(*cp))
                return cmd_status(sk, index, MGMT_OP_SET_FAST_CONNECTABLE,
-                                                               EINVAL);
+                                               MGMT_STATUS_INVALID_PARAMS);
  
        hdev = hci_dev_get(index);
        if (!hdev)
                return cmd_status(sk, index, MGMT_OP_SET_FAST_CONNECTABLE,
-                                                               ENODEV);
+                                               MGMT_STATUS_INVALID_PARAMS);
  
        hci_dev_lock(hdev);
  
                                                sizeof(acp), &acp);
        if (err < 0) {
                err = cmd_status(sk, index, MGMT_OP_SET_FAST_CONNECTABLE,
-                                                               -err);
+                                                       MGMT_STATUS_FAILED);
                goto done;
        }
  
        err = hci_send_cmd(hdev, HCI_OP_WRITE_PAGE_SCAN_TYPE, 1, &type);
        if (err < 0) {
                err = cmd_status(sk, index, MGMT_OP_SET_FAST_CONNECTABLE,
-                                                               -err);
+                                                       MGMT_STATUS_FAILED);
                goto done;
        }
  
@@@ -1903,10 -2150,18 +2151,18 @@@ int mgmt_control(struct sock *sk, struc
                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);
                                                                        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);
                break;
        default:
                BT_DBG("Unknown op %u", opcode);
-               err = cmd_status(sk, index, opcode, 0x01);
+               err = cmd_status(sk, index, opcode,
+                                               MGMT_STATUS_UNKNOWN_COMMAND);
                break;
        }
  
@@@ -2062,13 -2318,15 +2319,15 @@@ int mgmt_connectable(struct hci_dev *hd
  
  int mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status)
  {
+       u8 mgmt_err = mgmt_status(status);
        if (scan & SCAN_PAGE)
                mgmt_pending_foreach(MGMT_OP_SET_CONNECTABLE, hdev,
-                                               cmd_status_rsp, &status);
+                                               cmd_status_rsp, &mgmt_err);
  
        if (scan & SCAN_INQUIRY)
                mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, hdev,
-                                               cmd_status_rsp, &status);
+                                               cmd_status_rsp, &mgmt_err);
  
        return 0;
  }
@@@ -2089,12 -2347,13 +2348,13 @@@ int mgmt_new_link_key(struct hci_dev *h
        return mgmt_event(MGMT_EV_NEW_LINK_KEY, hdev, &ev, sizeof(ev), NULL);
  }
  
- int mgmt_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type)
+ int mgmt_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
+                                                               u8 addr_type)
  {
        struct mgmt_addr_info ev;
  
        bacpy(&ev.bdaddr, bdaddr);
-       ev.type = link_to_mgmt(link_type);
+       ev.type = link_to_mgmt(link_type, addr_type);
  
        return mgmt_event(MGMT_EV_CONNECTED, hdev, &ev, sizeof(ev), NULL);
  }
@@@ -2106,6 -2365,7 +2366,7 @@@ static void disconnect_rsp(struct pendi
        struct mgmt_rp_disconnect rp;
  
        bacpy(&rp.bdaddr, &cp->bdaddr);
+       rp.status = 0;
  
        cmd_complete(cmd->sk, cmd->index, MGMT_OP_DISCONNECT, &rp, sizeof(rp));
  
        mgmt_pending_remove(cmd);
  }
  
- int mgmt_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
+ static void remove_keys_rsp(struct pending_cmd *cmd, void *data)
+ {
+       u8 *status = data;
+       struct mgmt_cp_remove_keys *cp = cmd->param;
+       struct mgmt_rp_remove_keys rp;
+       memset(&rp, 0, sizeof(rp));
+       bacpy(&rp.bdaddr, &cp->bdaddr);
+       if (status != NULL)
+               rp.status = *status;
+       cmd_complete(cmd->sk, cmd->index, MGMT_OP_REMOVE_KEYS, &rp,
+                                                               sizeof(rp));
+       mgmt_pending_remove(cmd);
+ }
+ int mgmt_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
+                                                               u8 addr_type)
  {
        struct mgmt_addr_info ev;
        struct sock *sk = NULL;
        mgmt_pending_foreach(MGMT_OP_DISCONNECT, hdev, disconnect_rsp, &sk);
  
        bacpy(&ev.bdaddr, bdaddr);
-       ev.type = link_to_mgmt(type);
+       ev.type = link_to_mgmt(link_type, addr_type);
  
        err = mgmt_event(MGMT_EV_DISCONNECTED, hdev, &ev, sizeof(ev), sk);
  
        if (sk)
                sock_put(sk);
  
+       mgmt_pending_foreach(MGMT_OP_REMOVE_KEYS, hdev, remove_keys_rsp, NULL);
        return err;
  }
  
- int mgmt_disconnect_failed(struct hci_dev *hdev)
+ int mgmt_disconnect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 status)
  {
        struct pending_cmd *cmd;
+       u8 mgmt_err = mgmt_status(status);
        int err;
  
        cmd = mgmt_pending_find(MGMT_OP_DISCONNECT, hdev);
        if (!cmd)
                return -ENOENT;
  
-       err = cmd_status(cmd->sk, hdev->id, MGMT_OP_DISCONNECT, EIO);
+       if (bdaddr) {
+               struct mgmt_rp_disconnect rp;
+               bacpy(&rp.bdaddr, bdaddr);
+               rp.status = status;
+               err = cmd_complete(cmd->sk, cmd->index, MGMT_OP_DISCONNECT,
+                                                       &rp, sizeof(rp));
+       } else
+               err = cmd_status(cmd->sk, hdev->id, MGMT_OP_DISCONNECT,
+                                                               mgmt_err);
  
        mgmt_pending_remove(cmd);
  
        return err;
  }
  
- int mgmt_connect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type,
-                                                               u8 status)
+ int mgmt_connect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
+                                               u8 addr_type, u8 status)
  {
        struct mgmt_ev_connect_failed ev;
  
        bacpy(&ev.addr.bdaddr, bdaddr);
-       ev.addr.type = link_to_mgmt(type);
-       ev.status = status;
+       ev.addr.type = link_to_mgmt(link_type, addr_type);
+       ev.status = mgmt_status(status);
  
        return mgmt_event(MGMT_EV_CONNECT_FAILED, hdev, &ev, sizeof(ev), NULL);
  }
@@@ -2185,7 -2476,7 +2477,7 @@@ int mgmt_pin_code_reply_complete(struc
                return -ENOENT;
  
        bacpy(&rp.bdaddr, bdaddr);
-       rp.status = status;
+       rp.status = mgmt_status(status);
  
        err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_REPLY, &rp,
                                                                sizeof(rp));
@@@ -2207,7 -2498,7 +2499,7 @@@ int mgmt_pin_code_neg_reply_complete(st
                return -ENOENT;
  
        bacpy(&rp.bdaddr, bdaddr);
-       rp.status = status;
+       rp.status = mgmt_status(status);
  
        err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_NEG_REPLY, &rp,
                                                                sizeof(rp));
@@@ -2232,7 -2523,19 +2524,19 @@@ int mgmt_user_confirm_request(struct hc
                                                                        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;
                return -ENOENT;
  
        bacpy(&rp.bdaddr, bdaddr);
-       rp.status = status;
+       rp.status = mgmt_status(status);
        err = cmd_complete(cmd->sk, hdev->id, opcode, &rp, sizeof(rp));
  
        mgmt_pending_remove(cmd);
  int mgmt_user_confirm_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
                                                                u8 status)
  {
-       return confirm_reply_complete(hdev, bdaddr, 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, 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;
  
        bacpy(&ev.bdaddr, bdaddr);
-       ev.status = status;
+       ev.status = mgmt_status(status);
  
        return mgmt_event(MGMT_EV_AUTH_FAILED, hdev, &ev, sizeof(ev), NULL);
  }
@@@ -2291,7 -2608,7 +2609,7 @@@ int mgmt_set_local_name_complete(struc
  
        if (status) {
                err = cmd_status(cmd->sk, hdev->id, MGMT_OP_SET_LOCAL_NAME,
-                                                                       EIO);
+                                                       mgmt_status(status));
                goto failed;
        }
  
@@@ -2326,7 -2643,8 +2644,8 @@@ int mgmt_read_local_oob_data_reply_comp
  
        if (status) {
                err = cmd_status(cmd->sk, hdev->id,
-                                       MGMT_OP_READ_LOCAL_OOB_DATA, EIO);
+                                               MGMT_OP_READ_LOCAL_OOB_DATA,
+                                               mgmt_status(status));
        } else {
                struct mgmt_rp_read_local_oob_data rp;
  
        return err;
  }
  
- int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type,
-                                       u8 *dev_class, s8 rssi, u8 *eir)
+ int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
+                               u8 addr_type, u8 *dev_class, s8 rssi, u8 *eir)
  {
        struct mgmt_ev_device_found ev;
  
        memset(&ev, 0, sizeof(ev));
  
        bacpy(&ev.addr.bdaddr, bdaddr);
-       ev.addr.type = link_to_mgmt(type);
+       ev.addr.type = link_to_mgmt(link_type, addr_type);
        ev.rssi = rssi;
  
        if (eir)
@@@ -2375,7 -2693,7 +2694,7 @@@ int mgmt_remote_name(struct hci_dev *hd
        return mgmt_event(MGMT_EV_REMOTE_NAME, hdev, &ev, sizeof(ev), NULL);
  }
  
- int mgmt_inquiry_failed(struct hci_dev *hdev, u8 status)
+ int mgmt_start_discovery_failed(struct hci_dev *hdev, u8 status)
  {
        struct pending_cmd *cmd;
        int err;
        if (!cmd)
                return -ENOENT;
  
+       err = cmd_status(cmd->sk, hdev->id, cmd->opcode, mgmt_status(status));
+       mgmt_pending_remove(cmd);
+       return err;
+ }
+ int mgmt_stop_discovery_failed(struct hci_dev *hdev, u8 status)
+ {
+       struct pending_cmd *cmd;
+       int err;
+       cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, hdev);
+       if (!cmd)
+               return -ENOENT;
        err = cmd_status(cmd->sk, hdev->id, cmd->opcode, status);
        mgmt_pending_remove(cmd);