]> 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)
18 files changed:
drivers/bluetooth/Kconfig
drivers/bluetooth/btmrvl_sdio.c
drivers/bluetooth/btusb.c
drivers/bluetooth/hci_vhci.c
include/net/bluetooth/bluetooth.h
include/net/bluetooth/hci.h
include/net/bluetooth/hci_core.h
include/net/bluetooth/l2cap.h
include/net/bluetooth/mgmt.h
net/bluetooth/bnep/core.c
net/bluetooth/cmtp/core.c
net/bluetooth/hci_conn.c
net/bluetooth/hci_core.c
net/bluetooth/hci_event.c
net/bluetooth/l2cap_core.c
net/bluetooth/l2cap_sock.c
net/bluetooth/mgmt.c
net/bluetooth/smp.c

index 11b41fd40c275b0f61aa77120dfab5b14219564a..5ccf142ef0b8139f47a95b75b13bdb5086b034a7 100644 (file)
@@ -188,7 +188,7 @@ config BT_MRVL
          The core driver to support Marvell Bluetooth devices.
 
          This driver is required if you want to support
-         Marvell Bluetooth devices, such as 8688/8787.
+         Marvell Bluetooth devices, such as 8688/8787/8797.
 
          Say Y here to compile Marvell Bluetooth driver
          into the kernel or say M to compile it as module.
@@ -201,8 +201,8 @@ config BT_MRVL_SDIO
          The driver for Marvell Bluetooth chipsets with SDIO interface.
 
          This driver is required if you want to use Marvell Bluetooth
-         devices with SDIO interface. Currently SD8688/SD8787 chipsets are
-         supported.
+         devices with SDIO interface. Currently SD8688/SD8787/SD8797
+         chipsets are supported.
 
          Say Y here to compile support for Marvell BT-over-SDIO driver
          into the kernel or say M to compile it as module.
index 9ef48167e2cf8ba64c790d4846a2d297e84c6fd0..27b74b0d547b540043fd318917e2cb5d6ebe0591 100644 (file)
@@ -65,7 +65,7 @@ static const struct btmrvl_sdio_card_reg btmrvl_reg_8688 = {
        .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 +92,14 @@ static const struct btmrvl_sdio_device btmrvl_sdio_sd8688 = {
 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 +110,9 @@ static const struct sdio_device_id btmrvl_sdio_ids[] = {
        /* 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 +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 2bd87d45f1c2313be3efc5a3728a8fcbe687b0d7..ea5ad1cbbd3d80be1b1277b414dc2417d82bef00 100644 (file)
@@ -785,9 +785,8 @@ skip_waking:
                usb_mark_last_busy(data->udev);
        }
 
-       usb_free_urb(urb);
-
 done:
+       usb_free_urb(urb);
        return err;
 }
 
index 2e302a11ab5528ccd078f41f9439c1067fd9704f..2ed6ab1c6e1b3a9e7a4b558d24b4f73ec6a8ad5f 100644 (file)
@@ -41,6 +41,8 @@
 
 #define VERSION "1.3"
 
+static bool amp;
+
 struct vhci_data {
        struct hci_dev *hdev;
 
@@ -239,6 +241,9 @@ static int vhci_open(struct inode *inode, struct file *file)
        hdev->bus = HCI_VIRTUAL;
        hdev->driver_data = data;
 
+       if (amp)
+               hdev->dev_type = HCI_AMP;
+
        hdev->open     = vhci_open_dev;
        hdev->close    = vhci_close_dev;
        hdev->flush    = vhci_flush;
@@ -303,6 +308,9 @@ static void __exit vhci_exit(void)
 module_init(vhci_init);
 module_exit(vhci_exit);
 
+module_param(amp, bool, 0644);
+MODULE_PARM_DESC(amp, "Create AMP controller device");
+
 MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
 MODULE_DESCRIPTION("Bluetooth virtual HCI driver ver " VERSION);
 MODULE_VERSION(VERSION);
index 835f3b229b8450d53f2ec434078dd9a3c0e8be52..980e59f37d4f8cb6b487570189b328683d311b5b 100644 (file)
 #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
 
index 139ce2aa6eee700ba396343a268eda4962daa142..67ad984303484bfa0b87776fd08e3e812083c42a 100644 (file)
@@ -88,6 +88,14 @@ enum {
        HCI_RESET,
 };
 
+/*
+ * BR/EDR and/or LE controller flags: the flags defined here should represent
+ * states from the controller.
+ */
+enum {
+       HCI_LE_SCAN,
+};
+
 /* HCI ioctl defines */
 #define HCIDEVUP       _IOW('H', 201, int)
 #define HCIDEVDOWN     _IOW('H', 202, int)
@@ -453,6 +461,14 @@ struct hci_rp_user_confirm_reply {
 
 #define HCI_OP_USER_CONFIRM_NEG_REPLY  0x042d
 
+#define HCI_OP_USER_PASSKEY_REPLY              0x042e
+struct hci_cp_user_passkey_reply {
+       bdaddr_t bdaddr;
+       __le32  passkey;
+} __packed;
+
+#define HCI_OP_USER_PASSKEY_NEG_REPLY  0x042f
+
 #define HCI_OP_REMOTE_OOB_DATA_REPLY   0x0430
 struct hci_cp_remote_oob_data_reply {
        bdaddr_t bdaddr;
@@ -669,6 +685,12 @@ struct hci_rp_read_local_oob_data {
 
 #define HCI_OP_READ_INQ_RSP_TX_POWER   0x0c58
 
+#define HCI_OP_READ_FLOW_CONTROL_MODE  0x0c66
+struct hci_rp_read_flow_control_mode {
+       __u8     status;
+       __u8     mode;
+} __packed;
+
 #define HCI_OP_WRITE_LE_HOST_SUPPORTED 0x0c6d
 struct hci_cp_write_le_host_supported {
        __u8 le;
@@ -760,6 +782,15 @@ struct hci_rp_le_read_buffer_size {
        __u8     le_max_pkt;
 } __packed;
 
+#define HCI_OP_LE_SET_SCAN_PARAM       0x200b
+struct hci_cp_le_set_scan_param {
+       __u8    type;
+       __le16  interval;
+       __le16  window;
+       __u8    own_address_type;
+       __u8    filter_policy;
+} __packed;
+
 #define HCI_OP_LE_SET_SCAN_ENABLE      0x200c
 struct hci_cp_le_set_scan_enable {
        __u8     enable;
@@ -1076,6 +1107,11 @@ struct hci_ev_user_confirm_req {
        __le32          passkey;
 } __packed;
 
+#define HCI_EV_USER_PASSKEY_REQUEST    0x34
+struct hci_ev_user_passkey_req {
+       bdaddr_t        bdaddr;
+} __packed;
+
 #define HCI_EV_REMOTE_OOB_DATA_REQUEST 0x35
 struct hci_ev_remote_oob_data_request {
        bdaddr_t bdaddr;
@@ -1331,4 +1367,6 @@ struct hci_inquiry_req {
 };
 #define IREQ_CACHE_FLUSH 0x0001
 
+extern int enable_hs;
+
 #endif /* __HCI_H */
index f333e7682607b1dbcc86871e3d0fd6c93a94c6cf..ea4395f1d26000214027baee3ed401353cb671b3 100644 (file)
@@ -170,6 +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;
@@ -250,6 +252,8 @@ struct hci_dev {
 
        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);
@@ -917,11 +921,13 @@ int mgmt_connectable(struct hci_dev *hdev, u8 connectable);
 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 +939,20 @@ int mgmt_user_confirm_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
                                                                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 875021ad0675098e6e9bb5cfa1cfc5dea170bcad..30719eb2e77cf737923e0d459a0b549eb015dae9 100644 (file)
@@ -792,7 +792,6 @@ static inline __u8 __ctrl_size(struct l2cap_chan *chan)
 }
 
 extern int disable_ertm;
-extern int enable_hs;
 
 int l2cap_init_sockets(void);
 void l2cap_cleanup_sockets(void);
@@ -810,5 +809,6 @@ int l2cap_chan_connect(struct l2cap_chan *chan);
 int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,
                                                                u32 priority);
 void l2cap_chan_busy(struct l2cap_chan *chan, int busy);
+int l2cap_chan_check_security(struct l2cap_chan *chan);
 
 #endif /* __L2CAP_H */
index 3e320c9cae8ffc7238d1c1197dc45c828a90931b..3b6880690a78ea0bb970cb6db2ed1299bb3bfac8 100644 (file)
 
 #define MGMT_INDEX_NONE                        0xFFFF
 
+#define MGMT_STATUS_SUCCESS            0x00
+#define MGMT_STATUS_UNKNOWN_COMMAND    0x01
+#define MGMT_STATUS_NOT_CONNECTED      0x02
+#define MGMT_STATUS_FAILED             0x03
+#define MGMT_STATUS_CONNECT_FAILED     0x04
+#define MGMT_STATUS_AUTH_FAILED                0x05
+#define MGMT_STATUS_NOT_PAIRED         0x06
+#define MGMT_STATUS_NO_RESOURCES       0x07
+#define MGMT_STATUS_TIMEOUT            0x08
+#define MGMT_STATUS_ALREADY_CONNECTED  0x09
+#define MGMT_STATUS_BUSY               0x0a
+#define MGMT_STATUS_REJECTED           0x0b
+#define MGMT_STATUS_NOT_SUPPORTED      0x0c
+#define MGMT_STATUS_INVALID_PARAMS     0x0d
+#define MGMT_STATUS_DISCONNECTED       0x0e
+#define MGMT_STATUS_NOT_POWERED                0x0f
+
 struct mgmt_hdr {
        __le16 opcode;
        __le16 index;
@@ -119,6 +136,10 @@ struct mgmt_cp_remove_keys {
        bdaddr_t bdaddr;
        __u8 disconnect;
 } __packed;
+struct mgmt_rp_remove_keys {
+       bdaddr_t bdaddr;
+       __u8 status;
+};
 
 #define MGMT_OP_DISCONNECT             0x000F
 struct mgmt_cp_disconnect {
@@ -126,11 +147,12 @@ struct mgmt_cp_disconnect {
 } __packed;
 struct mgmt_rp_disconnect {
        bdaddr_t bdaddr;
+       __u8 status;
 } __packed;
 
 #define MGMT_ADDR_BREDR                        0x00
-#define MGMT_ADDR_LE                   0x01
-#define MGMT_ADDR_BREDR_LE             0x02
+#define MGMT_ADDR_LE_PUBLIC            0x01
+#define MGMT_ADDR_LE_RANDOM            0x02
 #define MGMT_ADDR_INVALID              0xff
 
 struct mgmt_addr_info {
@@ -167,11 +189,11 @@ struct mgmt_cp_set_io_capability {
 
 #define MGMT_OP_PAIR_DEVICE            0x0014
 struct mgmt_cp_pair_device {
-       bdaddr_t bdaddr;
+       struct mgmt_addr_info addr;
        __u8 io_cap;
 } __packed;
 struct mgmt_rp_pair_device {
-       bdaddr_t bdaddr;
+       struct mgmt_addr_info addr;
        __u8 status;
 } __packed;
 
@@ -210,6 +232,9 @@ struct mgmt_cp_remove_remote_oob_data {
 } __packed;
 
 #define MGMT_OP_START_DISCOVERY                0x001B
+struct mgmt_cp_start_discovery {
+       __u8 type;
+} __packed;
 
 #define MGMT_OP_STOP_DISCOVERY         0x001C
 
@@ -228,6 +253,17 @@ struct mgmt_cp_set_fast_connectable {
        __u8 enable;
 } __packed;
 
+#define MGMT_OP_USER_PASSKEY_REPLY     0x0020
+struct mgmt_cp_user_passkey_reply {
+       bdaddr_t bdaddr;
+       __le32 passkey;
+} __packed;
+
+#define MGMT_OP_USER_PASSKEY_NEG_REPLY 0x0021
+struct mgmt_cp_user_passkey_neg_reply {
+       bdaddr_t bdaddr;
+} __packed;
+
 #define MGMT_EV_CMD_COMPLETE           0x0001
 struct mgmt_ev_cmd_complete {
        __le16 opcode;
@@ -322,3 +358,8 @@ struct mgmt_ev_device_blocked {
 struct mgmt_ev_device_unblocked {
        bdaddr_t bdaddr;
 } __packed;
+
+#define MGMT_EV_USER_PASSKEY_REQUEST   0x0017
+struct mgmt_ev_user_passkey_request {
+       bdaddr_t bdaddr;
+} __packed;
index a6cd856046ab247cb29da2d9831077153da8a642..42d53b85a80826c3ddc5af51c79a8684293a25f2 100644 (file)
@@ -77,17 +77,12 @@ static struct bnep_session *__bnep_get_session(u8 *dst)
 
 static void __bnep_link_session(struct bnep_session *s)
 {
-       /* It's safe to call __module_get() here because sessions are added
-          by the socket layer which has to hold the reference to this module.
-        */
-       __module_get(THIS_MODULE);
        list_add(&s->list, &bnep_session_list);
 }
 
 static void __bnep_unlink_session(struct bnep_session *s)
 {
        list_del(&s->list);
-       module_put(THIS_MODULE);
 }
 
 static int bnep_send(struct bnep_session *s, void *data, size_t len)
@@ -528,6 +523,7 @@ static int bnep_session(void *arg)
 
        up_write(&bnep_session_sem);
        free_netdev(dev);
+       module_put_and_exit(0);
        return 0;
 }
 
@@ -614,9 +610,11 @@ int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock)
 
        __bnep_link_session(s);
 
+       __module_get(THIS_MODULE);
        s->task = kthread_run(bnep_session, s, "kbnepd %s", dev->name);
        if (IS_ERR(s->task)) {
                /* Session thread start failed, gotta cleanup. */
+               module_put(THIS_MODULE);
                unregister_netdev(dev);
                __bnep_unlink_session(s);
                err = PTR_ERR(s->task);
index 9e8940b24bbad73c67dd37fd23b0e787090cacd8..6c9c1fd601cac41e879c3e041f103ee645ebd458 100644 (file)
@@ -65,14 +65,12 @@ static struct cmtp_session *__cmtp_get_session(bdaddr_t *bdaddr)
 
 static void __cmtp_link_session(struct cmtp_session *session)
 {
-       __module_get(THIS_MODULE);
        list_add(&session->list, &cmtp_session_list);
 }
 
 static void __cmtp_unlink_session(struct cmtp_session *session)
 {
        list_del(&session->list);
-       module_put(THIS_MODULE);
 }
 
 static void __cmtp_copy_session(struct cmtp_session *session, struct cmtp_conninfo *ci)
@@ -325,6 +323,7 @@ static int cmtp_session(void *arg)
        up_write(&cmtp_session_sem);
 
        kfree(session);
+       module_put_and_exit(0);
        return 0;
 }
 
@@ -374,9 +373,11 @@ int cmtp_add_connection(struct cmtp_connadd_req *req, struct socket *sock)
 
        __cmtp_link_session(session);
 
+       __module_get(THIS_MODULE);
        session->task = kthread_run(cmtp_session, session, "kcmtpd_ctr_%d",
                                                                session->num);
        if (IS_ERR(session->task)) {
+               module_put(THIS_MODULE);
                err = PTR_ERR(session->task);
                goto unlink;
        }
index de0b93e45980138235044550be69f960cd8cf9ab..b328ac611ccdf370e9a8c6b9c8fa96f218622b59 100644 (file)
@@ -123,7 +123,7 @@ static void hci_acl_connect_cancel(struct hci_conn *conn)
 
        BT_DBG("%p", conn);
 
-       if (conn->hdev->hci_ver < 2)
+       if (conn->hdev->hci_ver < BLUETOOTH_VER_1_2)
                return;
 
        bacpy(&cp.bdaddr, &conn->dst);
index fb3feeb185d7098359bdd71b0156fa8a67d0157f..ce3727ecc0c40b80d9d7d5681fafbf5efbf3f7ef 100644 (file)
@@ -54,6 +54,8 @@
 
 #define AUTO_OFF_TIMEOUT 2000
 
+int enable_hs;
+
 static void hci_cmd_task(unsigned long arg);
 static void hci_rx_task(unsigned long arg);
 static void hci_tx_task(unsigned long arg);
@@ -228,18 +230,6 @@ static void hci_init_req(struct hci_dev *hdev, unsigned long opt)
        /* Read Buffer Size (ACL mtu, max pkt, etc.) */
        hci_send_cmd(hdev, HCI_OP_READ_BUFFER_SIZE, 0, NULL);
 
-#if 0
-       /* Host buffer size */
-       {
-               struct hci_cp_host_buffer_size cp;
-               cp.acl_mtu = cpu_to_le16(HCI_MAX_ACL_SIZE);
-               cp.sco_mtu = HCI_MAX_SCO_SIZE;
-               cp.acl_max_pkt = cpu_to_le16(0xffff);
-               cp.sco_max_pkt = cpu_to_le16(0xffff);
-               hci_send_cmd(hdev, HCI_OP_HOST_BUFFER_SIZE, sizeof(cp), &cp);
-       }
-#endif
-
        /* Read BD Address */
        hci_send_cmd(hdev, HCI_OP_READ_BD_ADDR, 0, NULL);
 
@@ -521,8 +511,9 @@ int hci_dev_open(__u16 dev)
        if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks))
                set_bit(HCI_RAW, &hdev->flags);
 
-       /* Treat all non BR/EDR controllers as raw devices for now */
-       if (hdev->dev_type != HCI_BREDR)
+       /* Treat all non BR/EDR controllers as raw devices if
+          enable_hs is not set */
+       if (hdev->dev_type != HCI_BREDR && !enable_hs)
                set_bit(HCI_RAW, &hdev->flags);
 
        if (hdev->open(hdev)) {
@@ -1336,14 +1327,12 @@ int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr)
 {
        struct bdaddr_list *entry;
 
-       if (bacmp(bdaddr, BDADDR_ANY) == 0) {
+       if (bacmp(bdaddr, BDADDR_ANY) == 0)
                return hci_blacklist_clear(hdev);
-       }
 
        entry = hci_blacklist_lookup(hdev, bdaddr);
-       if (!entry) {
+       if (!entry)
                return -ENOENT;
-       }
 
        list_del(&entry->list);
        kfree(entry);
@@ -1451,12 +1440,13 @@ int hci_register_dev(struct hci_dev *hdev)
 
        sprintf(hdev->name, "hci%d", id);
        hdev->id = id;
-       list_add(&hdev->list, head);
+       list_add_tail(&hdev->list, head);
 
        atomic_set(&hdev->refcnt, 1);
        spin_lock_init(&hdev->lock);
 
        hdev->flags = 0;
+       hdev->dev_flags = 0;
        hdev->pkt_type  = (HCI_DM1 | HCI_DH1 | HCI_HV1);
        hdev->esco_type = (ESCO_HV1);
        hdev->link_mode = (HCI_LM_ACCEPT);
@@ -2614,3 +2604,6 @@ int hci_cancel_inquiry(struct hci_dev *hdev)
 
        return hci_send_cmd(hdev, HCI_OP_INQUIRY_CANCEL, 0, NULL);
 }
+
+module_param(enable_hs, bool, 0644);
+MODULE_PARM_DESC(enable_hs, "Enable High Speed");
index a89cf1f24e47697d14f009900ac8a3ee8fe0e549..35cb56ed3b0b05e988a4229517df793ca242f7eb 100644 (file)
@@ -55,8 +55,12 @@ static void hci_cc_inquiry_cancel(struct hci_dev *hdev, struct sk_buff *skb)
 
        BT_DBG("%s status 0x%x", hdev->name, status);
 
-       if (status)
+       if (status) {
+               hci_dev_lock(hdev);
+               mgmt_stop_discovery_failed(hdev, status);
+               hci_dev_unlock(hdev);
                return;
+       }
 
        clear_bit(HCI_INQUIRY, &hdev->flags);
 
@@ -190,6 +194,8 @@ static void hci_cc_reset(struct hci_dev *hdev, struct sk_buff *skb)
        clear_bit(HCI_RESET, &hdev->flags);
 
        hci_req_complete(hdev, HCI_OP_RESET, status);
+
+       hdev->dev_flags = 0;
 }
 
 static void hci_cc_write_local_name(struct hci_dev *hdev, struct sk_buff *skb)
@@ -494,7 +500,7 @@ static void hci_setup_event_mask(struct hci_dev *hdev)
 
        /* CSR 1.1 dongles does not accept any bitfield so don't try to set
         * any event mask for pre 1.2 devices */
-       if (hdev->lmp_ver <= 1)
+       if (hdev->hci_ver < BLUETOOTH_VER_1_2)
                return;
 
        events[4] |= 0x01; /* Flow Specification Complete */
@@ -558,7 +564,7 @@ static void hci_setup(struct hci_dev *hdev)
 {
        hci_setup_event_mask(hdev);
 
-       if (hdev->lmp_ver > 1)
+       if (hdev->hci_ver > BLUETOOTH_VER_1_1)
                hci_send_cmd(hdev, HCI_OP_READ_LOCAL_COMMANDS, 0, NULL);
 
        if (hdev->features[6] & LMP_SIMPLE_PAIR) {
@@ -713,6 +719,21 @@ static void hci_cc_read_local_ext_features(struct hci_dev *hdev,
        hci_req_complete(hdev, HCI_OP_READ_LOCAL_EXT_FEATURES, rp->status);
 }
 
+static void hci_cc_read_flow_control_mode(struct hci_dev *hdev,
+                                               struct sk_buff *skb)
+{
+       struct hci_rp_read_flow_control_mode *rp = (void *) skb->data;
+
+       BT_DBG("%s status 0x%x", hdev->name, rp->status);
+
+       if (rp->status)
+               return;
+
+       hdev->flow_ctl_mode = rp->mode;
+
+       hci_req_complete(hdev, HCI_OP_READ_FLOW_CONTROL_MODE, rp->status);
+}
+
 static void hci_cc_read_buffer_size(struct hci_dev *hdev, struct sk_buff *skb)
 {
        struct hci_rp_read_buffer_size *rp = (void *) skb->data;
@@ -927,6 +948,37 @@ static void hci_cc_user_confirm_neg_reply(struct hci_dev *hdev,
        hci_dev_unlock(hdev);
 }
 
+static void hci_cc_user_passkey_reply(struct hci_dev *hdev, struct sk_buff *skb)
+{
+       struct hci_rp_user_confirm_reply *rp = (void *) skb->data;
+
+       BT_DBG("%s status 0x%x", hdev->name, rp->status);
+
+       hci_dev_lock(hdev);
+
+       if (test_bit(HCI_MGMT, &hdev->flags))
+               mgmt_user_passkey_reply_complete(hdev, &rp->bdaddr,
+                                                               rp->status);
+
+       hci_dev_unlock(hdev);
+}
+
+static void hci_cc_user_passkey_neg_reply(struct hci_dev *hdev,
+                                                       struct sk_buff *skb)
+{
+       struct hci_rp_user_confirm_reply *rp = (void *) skb->data;
+
+       BT_DBG("%s status 0x%x", hdev->name, rp->status);
+
+       hci_dev_lock(hdev);
+
+       if (test_bit(HCI_MGMT, &hdev->flags))
+               mgmt_user_passkey_neg_reply_complete(hdev, &rp->bdaddr,
+                                                               rp->status);
+
+       hci_dev_unlock(hdev);
+}
+
 static void hci_cc_read_local_oob_data_reply(struct hci_dev *hdev,
                                                        struct sk_buff *skb)
 {
@@ -940,6 +992,13 @@ static void hci_cc_read_local_oob_data_reply(struct hci_dev *hdev,
        hci_dev_unlock(hdev);
 }
 
+static void hci_cc_le_set_scan_param(struct hci_dev *hdev, struct sk_buff *skb)
+{
+       __u8 status = *((__u8 *) skb->data);
+
+       BT_DBG("%s status 0x%x", hdev->name, status);
+}
+
 static void hci_cc_le_set_scan_enable(struct hci_dev *hdev,
                                        struct sk_buff *skb)
 {
@@ -956,12 +1015,16 @@ static void hci_cc_le_set_scan_enable(struct hci_dev *hdev,
                return;
 
        if (cp->enable == 0x01) {
+               set_bit(HCI_LE_SCAN, &hdev->dev_flags);
+
                del_timer(&hdev->adv_timer);
 
                hci_dev_lock(hdev);
                hci_adv_entries_clear(hdev);
                hci_dev_unlock(hdev);
        } else if (cp->enable == 0x00) {
+               clear_bit(HCI_LE_SCAN, &hdev->dev_flags);
+
                mod_timer(&hdev->adv_timer, jiffies + ADV_CLEAR_TIMEOUT);
        }
 }
@@ -1014,7 +1077,7 @@ static inline void hci_cs_inquiry(struct hci_dev *hdev, __u8 status)
                hci_conn_check_pending(hdev);
                hci_dev_lock(hdev);
                if (test_bit(HCI_MGMT, &hdev->flags))
-                       mgmt_inquiry_failed(hdev, status);
+                       mgmt_start_discovery_failed(hdev, status);
                hci_dev_unlock(hdev);
                return;
        }
@@ -1437,7 +1500,7 @@ static inline void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff *
                data.rssi               = 0x00;
                data.ssp_mode           = 0x00;
                hci_inquiry_cache_update(hdev, &data);
-               mgmt_device_found(hdev, &info->bdaddr, ACL_LINK,
+               mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00,
                                                info->dev_class, 0, NULL);
        }
 
@@ -1472,7 +1535,8 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s
                        conn->state = BT_CONFIG;
                        hci_conn_hold(conn);
                        conn->disc_timeout = HCI_DISCONN_TIMEOUT;
-                       mgmt_connected(hdev, &ev->bdaddr, conn->type);
+                       mgmt_connected(hdev, &ev->bdaddr, conn->type,
+                                                       conn->dst_type);
                } else
                        conn->state = BT_CONNECTED;
 
@@ -1494,7 +1558,7 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s
                }
 
                /* Set packet type for incoming connection */
-               if (!conn->out && hdev->hci_ver < 3) {
+               if (!conn->out && hdev->hci_ver < BLUETOOTH_VER_2_0) {
                        struct hci_cp_change_conn_ptype cp;
                        cp.handle = ev->handle;
                        cp.pkt_type = cpu_to_le16(conn->pkt_type);
@@ -1505,7 +1569,7 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s
                conn->state = BT_CLOSED;
                if (conn->type == ACL_LINK)
                        mgmt_connect_failed(hdev, &ev->bdaddr, conn->type,
-                                                               ev->status);
+                                               conn->dst_type, ev->status);
        }
 
        if (conn->type == ACL_LINK)
@@ -1604,26 +1668,27 @@ static inline void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff
 
        BT_DBG("%s status %d", hdev->name, ev->status);
 
-       if (ev->status) {
-               hci_dev_lock(hdev);
-               mgmt_disconnect_failed(hdev);
-               hci_dev_unlock(hdev);
-               return;
-       }
-
        hci_dev_lock(hdev);
 
        conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle));
        if (!conn)
                goto unlock;
 
-       conn->state = BT_CLOSED;
+       if (ev->status == 0)
+               conn->state = BT_CLOSED;
 
-       if (conn->type == ACL_LINK || conn->type == LE_LINK)
-               mgmt_disconnected(hdev, &conn->dst, conn->type);
+       if (conn->type == ACL_LINK || conn->type == LE_LINK) {
+               if (ev->status != 0)
+                       mgmt_disconnect_failed(hdev, &conn->dst, ev->status);
+               else
+                       mgmt_disconnected(hdev, &conn->dst, conn->type,
+                                                       conn->dst_type);
+       }
 
-       hci_proto_disconn_cfm(conn, ev->reason);
-       hci_conn_del(conn);
+       if (ev->status == 0) {
+               hci_proto_disconn_cfm(conn, ev->reason);
+               hci_conn_del(conn);
+       }
 
 unlock:
        hci_dev_unlock(hdev);
@@ -1961,6 +2026,10 @@ static inline void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *sk
                hci_cc_write_ca_timeout(hdev, skb);
                break;
 
+       case HCI_OP_READ_FLOW_CONTROL_MODE:
+               hci_cc_read_flow_control_mode(hdev, skb);
+               break;
+
        case HCI_OP_READ_LOCAL_AMP_INFO:
                hci_cc_read_local_amp_info(hdev, skb);
                break;
@@ -2009,6 +2078,17 @@ static inline void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *sk
                hci_cc_user_confirm_neg_reply(hdev, skb);
                break;
 
+       case HCI_OP_USER_PASSKEY_REPLY:
+               hci_cc_user_passkey_reply(hdev, skb);
+               break;
+
+       case HCI_OP_USER_PASSKEY_NEG_REPLY:
+               hci_cc_user_passkey_neg_reply(hdev, skb);
+
+       case HCI_OP_LE_SET_SCAN_PARAM:
+               hci_cc_le_set_scan_param(hdev, skb);
+               break;
+
        case HCI_OP_LE_SET_SCAN_ENABLE:
                hci_cc_le_set_scan_enable(hdev, skb);
                break;
@@ -2096,7 +2176,7 @@ static inline void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb)
 
        case HCI_OP_DISCONNECT:
                if (ev->status != 0)
-                       mgmt_disconnect_failed(hdev);
+                       mgmt_disconnect_failed(hdev, NULL, ev->status);
                break;
 
        case HCI_OP_LE_CREATE_CONN:
@@ -2444,7 +2524,7 @@ static inline void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct
                        data.rssi               = info->rssi;
                        data.ssp_mode           = 0x00;
                        hci_inquiry_cache_update(hdev, &data);
-                       mgmt_device_found(hdev, &info->bdaddr, ACL_LINK,
+                       mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00,
                                                info->dev_class, info->rssi,
                                                NULL);
                }
@@ -2461,7 +2541,7 @@ static inline void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct
                        data.rssi               = info->rssi;
                        data.ssp_mode           = 0x00;
                        hci_inquiry_cache_update(hdev, &data);
-                       mgmt_device_found(hdev, &info->bdaddr, ACL_LINK,
+                       mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00,
                                                info->dev_class, info->rssi,
                                                NULL);
                }
@@ -2604,7 +2684,7 @@ static inline void hci_extended_inquiry_result_evt(struct hci_dev *hdev, struct
                data.rssi               = info->rssi;
                data.ssp_mode           = 0x01;
                hci_inquiry_cache_update(hdev, &data);
-               mgmt_device_found(hdev, &info->bdaddr, ACL_LINK,
+               mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00,
                                info->dev_class, info->rssi, info->data);
        }
 
@@ -2768,6 +2848,21 @@ unlock:
        hci_dev_unlock(hdev);
 }
 
+static inline void hci_user_passkey_request_evt(struct hci_dev *hdev,
+                                                       struct sk_buff *skb)
+{
+       struct hci_ev_user_passkey_req *ev = (void *) skb->data;
+
+       BT_DBG("%s", hdev->name);
+
+       hci_dev_lock(hdev);
+
+       if (test_bit(HCI_MGMT, &hdev->flags))
+               mgmt_user_passkey_request(hdev, &ev->bdaddr);
+
+       hci_dev_unlock(hdev);
+}
+
 static inline void hci_simple_pair_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
 {
        struct hci_ev_simple_pair_complete *ev = (void *) skb->data;
@@ -2868,14 +2963,15 @@ static inline void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff
        }
 
        if (ev->status) {
-               mgmt_connect_failed(hdev, &ev->bdaddr, conn->type, ev->status);
+               mgmt_connect_failed(hdev, &ev->bdaddr, conn->type,
+                                               conn->dst_type, ev->status);
                hci_proto_connect_cfm(conn, ev->status);
                conn->state = BT_CLOSED;
                hci_conn_del(conn);
                goto unlock;
        }
 
-       mgmt_connected(hdev, &ev->bdaddr, conn->type);
+       mgmt_connected(hdev, &ev->bdaddr, conn->type, conn->dst_type);
 
        conn->sec_level = BT_SECURITY_LOW;
        conn->handle = __le16_to_cpu(ev->handle);
@@ -3106,6 +3202,10 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
                hci_user_confirm_request_evt(hdev, skb);
                break;
 
+       case HCI_EV_USER_PASSKEY_REQUEST:
+               hci_user_passkey_request_evt(hdev, skb);
+               break;
+
        case HCI_EV_SIMPLE_PAIR_COMPLETE:
                hci_simple_pair_complete_evt(hdev, skb);
                break;
index e8a6837996cf311c3156c6c7b18ffc92cb0fba84..014fdec17113c74f64272bd3d66eb10081e809ca 100644 (file)
@@ -57,7 +57,6 @@
 #include <net/bluetooth/smp.h>
 
 int disable_ertm;
-int enable_hs;
 
 static u32 l2cap_feat_mask = L2CAP_FEAT_FIXED_CHAN;
 static u8 l2cap_fixed_chan[8] = { L2CAP_FC_L2CAP, };
@@ -97,7 +96,6 @@ static struct l2cap_chan *__l2cap_get_chan_by_dcid(struct l2cap_conn *conn, u16
                        return c;
        }
        return NULL;
-
 }
 
 static struct l2cap_chan *__l2cap_get_chan_by_scid(struct l2cap_conn *conn, u16 cid)
@@ -154,12 +152,9 @@ static struct l2cap_chan *__l2cap_global_chan_by_addr(__le16 psm, bdaddr_t *src)
 
        list_for_each_entry(c, &chan_list, global_l) {
                if (c->sport == psm && !bacmp(&bt_sk(c->sk)->src, src))
-                       goto found;
+                       return c;
        }
-
-       c = NULL;
-found:
-       return c;
+       return NULL;
 }
 
 int l2cap_add_psm(struct l2cap_chan *chan, bdaddr_t *src, __le16 psm)
@@ -234,8 +229,37 @@ static void l2cap_clear_timer(struct l2cap_chan *chan, struct timer_list *timer)
                chan_put(chan);
 }
 
+static char *state_to_string(int state)
+{
+       switch(state) {
+       case BT_CONNECTED:
+               return "BT_CONNECTED";
+       case BT_OPEN:
+               return "BT_OPEN";
+       case BT_BOUND:
+               return "BT_BOUND";
+       case BT_LISTEN:
+               return "BT_LISTEN";
+       case BT_CONNECT:
+               return "BT_CONNECT";
+       case BT_CONNECT2:
+               return "BT_CONNECT2";
+       case BT_CONFIG:
+               return "BT_CONFIG";
+       case BT_DISCONN:
+               return "BT_DISCONN";
+       case BT_CLOSED:
+               return "BT_CLOSED";
+       }
+
+       return "invalid state";
+}
+
 static void l2cap_state_change(struct l2cap_chan *chan, int state)
 {
+       BT_DBG("%p %s -> %s", chan, state_to_string(chan->state),
+                                               state_to_string(state));
+
        chan->state = state;
        chan->ops->state_change(chan->data, state);
 }
@@ -518,7 +542,7 @@ static inline u8 l2cap_get_auth_type(struct l2cap_chan *chan)
 }
 
 /* Service level security */
-static inline int l2cap_check_security(struct l2cap_chan *chan)
+int l2cap_chan_check_security(struct l2cap_chan *chan)
 {
        struct l2cap_conn *conn = chan->conn;
        __u8 auth_type;
@@ -664,7 +688,7 @@ static void l2cap_do_start(struct l2cap_chan *chan)
                if (!(conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE))
                        return;
 
-               if (l2cap_check_security(chan) &&
+               if (l2cap_chan_check_security(chan) &&
                                __l2cap_no_conn_pending(chan)) {
                        struct l2cap_conn_req req;
                        req.scid = cpu_to_le16(chan->scid);
@@ -754,7 +778,7 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
                if (chan->state == BT_CONNECT) {
                        struct l2cap_conn_req req;
 
-                       if (!l2cap_check_security(chan) ||
+                       if (!l2cap_chan_check_security(chan) ||
                                        !__l2cap_no_conn_pending(chan)) {
                                bh_unlock_sock(sk);
                                continue;
@@ -787,7 +811,7 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
                        rsp.scid = cpu_to_le16(chan->dcid);
                        rsp.dcid = cpu_to_le16(chan->scid);
 
-                       if (l2cap_check_security(chan)) {
+                       if (l2cap_chan_check_security(chan)) {
                                if (bt_sk(sk)->defer_setup) {
                                        struct sock *parent = bt_sk(sk)->parent;
                                        rsp.result = cpu_to_le16(L2CAP_CR_PEND);
@@ -1181,7 +1205,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan)
        if (hcon->state == BT_CONNECTED) {
                if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
                        __clear_chan_timer(chan);
-                       if (l2cap_check_security(chan))
+                       if (l2cap_chan_check_security(chan))
                                l2cap_state_change(chan, BT_CONNECTED);
                } else
                        l2cap_do_start(chan);
@@ -1318,14 +1342,12 @@ static void l2cap_retransmit_one_frame(struct l2cap_chan *chan, u16 tx_seq)
        if (!skb)
                return;
 
-       do {
-               if (bt_cb(skb)->tx_seq == tx_seq)
-                       break;
-
+       while (bt_cb(skb)->tx_seq != tx_seq) {
                if (skb_queue_is_last(&chan->tx_q, skb))
                        return;
 
-       } while ((skb = skb_queue_next(&chan->tx_q, skb)));
+               skb = skb_queue_next(&chan->tx_q, skb);
+       }
 
        if (chan->remote_max_tx &&
                        bt_cb(skb)->retries == chan->remote_max_tx) {
@@ -1906,7 +1928,7 @@ static void l2cap_add_opt_efs(void **ptr, struct l2cap_chan *chan)
 {
        struct l2cap_conf_efs efs;
 
-       switch(chan->mode) {
+       switch (chan->mode) {
        case L2CAP_MODE_ERTM:
                efs.id          = chan->local_id;
                efs.stype       = chan->local_stype;
@@ -2606,7 +2628,7 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd
        chan->ident = cmd->ident;
 
        if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE) {
-               if (l2cap_check_security(chan)) {
+               if (l2cap_chan_check_security(chan)) {
                        if (bt_sk(sk)->defer_setup) {
                                l2cap_state_change(chan, BT_CONNECT2);
                                result = L2CAP_CR_PEND;
@@ -3019,7 +3041,7 @@ static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, struct l2cap_cmd
 
        /* don't delete l2cap channel if sk is owned by user */
        if (sock_owned_by_user(sk)) {
-               l2cap_state_change(chan,BT_DISCONN);
+               l2cap_state_change(chan, BT_DISCONN);
                __clear_chan_timer(chan);
                __set_chan_timer(chan, L2CAP_DISC_TIMEOUT);
                bh_unlock_sock(sk);
@@ -3562,14 +3584,10 @@ static int l2cap_add_to_srej_queue(struct l2cap_chan *chan, struct sk_buff *skb,
        bt_cb(skb)->sar = sar;
 
        next_skb = skb_peek(&chan->srej_q);
-       if (!next_skb) {
-               __skb_queue_tail(&chan->srej_q, skb);
-               return 0;
-       }
 
        tx_seq_offset = __seq_offset(chan, tx_seq, chan->buffer_seq);
 
-       do {
+       while (next_skb) {
                if (bt_cb(next_skb)->tx_seq == tx_seq)
                        return -EINVAL;
 
@@ -3582,9 +3600,10 @@ static int l2cap_add_to_srej_queue(struct l2cap_chan *chan, struct sk_buff *skb,
                }
 
                if (skb_queue_is_last(&chan->srej_q, next_skb))
-                       break;
-
-       } while ((next_skb = skb_queue_next(&chan->srej_q, next_skb)));
+                       next_skb = NULL;
+               else
+                       next_skb = skb_queue_next(&chan->srej_q, next_skb);
+       }
 
        __skb_queue_tail(&chan->srej_q, skb);
 
@@ -3788,7 +3807,7 @@ static void l2cap_resend_srejframe(struct l2cap_chan *chan, u16 tx_seq)
        }
 }
 
-static void l2cap_send_srejframe(struct l2cap_chan *chan, u16 tx_seq)
+static int l2cap_send_srejframe(struct l2cap_chan *chan, u16 tx_seq)
 {
        struct srej_list *new;
        u32 control;
@@ -3799,6 +3818,9 @@ static void l2cap_send_srejframe(struct l2cap_chan *chan, u16 tx_seq)
                l2cap_send_sframe(chan, control);
 
                new = kzalloc(sizeof(struct srej_list), GFP_ATOMIC);
+               if (!new)
+                       return -ENOMEM;
+
                new->tx_seq = chan->expected_tx_seq;
 
                chan->expected_tx_seq = __next_seq(chan, chan->expected_tx_seq);
@@ -3807,6 +3829,8 @@ static void l2cap_send_srejframe(struct l2cap_chan *chan, u16 tx_seq)
        }
 
        chan->expected_tx_seq = __next_seq(chan, chan->expected_tx_seq);
+
+       return 0;
 }
 
 static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u32 rx_control, struct sk_buff *skb)
@@ -3877,7 +3901,12 @@ static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u32 rx_cont
                                        return 0;
                                }
                        }
-                       l2cap_send_srejframe(chan, tx_seq);
+
+                       err = l2cap_send_srejframe(chan, tx_seq);
+                       if (err < 0) {
+                               l2cap_send_disconn_req(chan->conn, chan, -err);
+                               return err;
+                       }
                }
        } else {
                expected_tx_seq_offset = __seq_offset(chan,
@@ -3899,7 +3928,11 @@ static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u32 rx_cont
 
                set_bit(CONN_SEND_PBIT, &chan->conn_state);
 
-               l2cap_send_srejframe(chan, tx_seq);
+               err = l2cap_send_srejframe(chan, tx_seq);
+               if (err < 0) {
+                       l2cap_send_disconn_req(chan->conn, chan, -err);
+                       return err;
+               }
 
                __clear_ack_timer(chan);
        }
@@ -3928,11 +3961,12 @@ expected:
                        l2cap_retransmit_frames(chan);
        }
 
-       __set_ack_timer(chan);
 
        chan->num_acked = (chan->num_acked + 1) % num_to_ack;
        if (chan->num_acked == num_to_ack - 1)
                l2cap_send_ack(chan);
+       else
+               __set_ack_timer(chan);
 
        return 0;
 
@@ -4768,6 +4802,3 @@ void l2cap_exit(void)
 
 module_param(disable_ertm, bool, 0644);
 MODULE_PARM_DESC(disable_ertm, "Disable enhanced retransmission mode");
-
-module_param(enable_hs, bool, 0644);
-MODULE_PARM_DESC(enable_hs, "Enable High Speed");
index e2e785c746306a6f4b79f10353b8f3fe7aa5d986..f73704321a77d0eaf74b3570da554855405a0358 100644 (file)
@@ -626,8 +626,13 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch
 
                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;
@@ -635,9 +640,14 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch
 
                        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;
 
index 94739d3c4f59ccabb5ed378c6795150a85c60c79..1ce549bae241809104194b8ed545a81c35821586 100644 (file)
@@ -22,6 +22,7 @@
 
 /* Bluetooth HCI Management interface */
 
+#include <linux/kernel.h>
 #include <linux/uaccess.h>
 #include <linux/module.h>
 #include <asm/unaligned.h>
@@ -44,6 +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 +252,8 @@ static int read_controller_info(struct sock *sk, u16 index)
 
        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 +366,15 @@ 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)
+{
+       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;
@@ -303,22 +387,25 @@ static int set_powered(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_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 +442,33 @@ static int set_discoverable(struct sock *sk, u16 index, unsigned char *data,
        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 +513,32 @@ static int set_connectable(struct sock *sk, u16 index, unsigned char *data,
        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 +593,6 @@ static int mgmt_event(u16 event, struct hci_dev *hdev, void *data,
        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)
 {
@@ -517,11 +605,13 @@ static int set_pairable(struct sock *sk, u16 index, unsigned char *data,
        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 +820,13 @@ static int add_uuid(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_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 +871,13 @@ static int remove_uuid(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_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);
 
@@ -805,7 +899,8 @@ static int remove_uuid(struct sock *sk, u16 index, unsigned char *data, u16 len)
        }
 
        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 +933,13 @@ static int set_dev_class(struct sock *sk, u16 index, unsigned char *data,
        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 +967,13 @@ static int set_service_cache(struct sock *sk, u16 index,  unsigned char *data,
        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 +1013,8 @@ static int load_link_keys(struct sock *sk, u16 index, unsigned char *data,
        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);
 
@@ -923,12 +1023,14 @@ static int load_link_keys(struct sock *sk, u16 index, unsigned char *data,
        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);
@@ -951,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);
 
@@ -962,41 +1066,64 @@ static int remove_keys(struct sock *sk, u16 index, unsigned char *data,
 {
        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 +1144,25 @@ static int disconnect(struct sock *sk, u16 index, unsigned char *data, u16 len)
        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;
        }
 
@@ -1040,7 +1171,8 @@ static int disconnect(struct sock *sk, u16 index, unsigned char *data, u16 len)
                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 +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 +1229,8 @@ static int get_connections(struct sock *sk, u16 index)
 
        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);
 
@@ -1111,7 +1251,7 @@ static int get_connections(struct sock *sk, u16 index)
        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 +1304,26 @@ static int pin_code_reply(struct sock *sk, u16 index, unsigned char *data,
        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;
        }
 
@@ -1191,7 +1335,7 @@ static int pin_code_reply(struct sock *sk, u16 index, unsigned char *data,
                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 +1374,18 @@ static int pin_code_neg_reply(struct sock *sk, u16 index, unsigned char *data,
 
        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 +1409,13 @@ static int set_io_capability(struct sock *sk, u16 index, unsigned char *data,
        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 +1453,8 @@ static void pairing_complete(struct pending_cmd *cmd, u8 status)
        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));
@@ -1325,27 +1472,22 @@ static void pairing_complete(struct pending_cmd *cmd, u8 status)
 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;
@@ -1355,11 +1497,13 @@ static int pair_device(struct sock *sk, u16 index, unsigned char *data, u16 len)
        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);
 
@@ -1369,22 +1513,29 @@ static int pair_device(struct sock *sk, u16 index, unsigned char *data, u16 len)
        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;
        }
 
@@ -1396,7 +1547,7 @@ static int pair_device(struct sock *sk, u16 index, unsigned char *data, u16 len)
        }
 
        /* 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 +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)
 {
@@ -1479,11 +1712,13 @@ static int set_local_name(struct sock *sk, u16 index, unsigned char *data,
        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 +1752,25 @@ static int read_local_oob_data(struct sock *sk, u16 index)
        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 +1802,20 @@ static int add_remote_oob_data(struct sock *sk, u16 index, unsigned char *data,
 
        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 +1837,19 @@ static int remove_remote_oob_data(struct sock *sk, u16 index,
 
        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);
@@ -1623,22 +1860,30 @@ 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, 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 +1914,8 @@ static int stop_discovery(struct sock *sk, u16 index)
 
        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 +1947,19 @@ static int block_device(struct sock *sk, u16 index, unsigned char *data,
 
        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 +1981,20 @@ static int unblock_device(struct sock *sk, u16 index, unsigned char *data,
 
        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 +2018,12 @@ static int set_fast_connectable(struct sock *sk, u16 index,
 
        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);
 
@@ -1793,14 +2041,14 @@ static int set_fast_connectable(struct sock *sk, u16 index,
                                                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 +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);
@@ -1922,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);
@@ -1939,7 +2195,8 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
                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 +2319,15 @@ int mgmt_connectable(struct hci_dev *hdev, u8 connectable)
 
 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 +2348,13 @@ int mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key,
        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 +2366,7 @@ static void disconnect_rsp(struct pending_cmd *cmd, void *data)
        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));
 
@@ -2115,7 +2376,25 @@ static void disconnect_rsp(struct pending_cmd *cmd, void *data)
        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;
@@ -2124,40 +2403,53 @@ int mgmt_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
        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 +2477,7 @@ int mgmt_pin_code_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
                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 +2499,7 @@ int mgmt_pin_code_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
                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 +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;
@@ -2244,7 +2548,7 @@ static int confirm_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
                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);
@@ -2255,23 +2559,37 @@ 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, 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 +2609,7 @@ int mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status)
 
        if (status) {
                err = cmd_status(cmd->sk, hdev->id, MGMT_OP_SET_LOCAL_NAME,
-                                                                       EIO);
+                                                       mgmt_status(status));
                goto failed;
        }
 
@@ -2326,7 +2644,8 @@ int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash,
 
        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;
 
@@ -2343,15 +2662,15 @@ int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash,
        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 +2694,7 @@ int mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *name)
        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;
@@ -2384,6 +2703,21 @@ int mgmt_inquiry_failed(struct hci_dev *hdev, u8 status)
        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);
 
index 94e94ca353848768e5bca28c3364a7b8554acdf3..0b96737d0ad36a27a7aaf8ed76dd9543ca0c2300 100644 (file)
@@ -232,6 +232,18 @@ static u8 check_enc_key_size(struct l2cap_conn *conn, __u8 max_key_size)
        return 0;
 }
 
+static void smp_failure(struct l2cap_conn *conn, u8 reason, u8 send)
+{
+       if (send)
+               smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, sizeof(reason),
+                                                               &reason);
+
+       clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->hcon->pend);
+       mgmt_auth_failed(conn->hcon->hdev, conn->dst, reason);
+       del_timer(&conn->security_timer);
+       smp_chan_destroy(conn);
+}
+
 static void confirm_work(struct work_struct *work)
 {
        struct smp_chan *smp = container_of(work, struct smp_chan, confirm);
@@ -270,8 +282,7 @@ static void confirm_work(struct work_struct *work)
        return;
 
 error:
-       smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, sizeof(reason), &reason);
-       smp_chan_destroy(conn);
+       smp_failure(conn, reason, 1);
 }
 
 static void random_work(struct work_struct *work)
@@ -354,8 +365,7 @@ static void random_work(struct work_struct *work)
        return;
 
 error:
-       smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, sizeof(reason), &reason);
-       smp_chan_destroy(conn);
+       smp_failure(conn, reason, 1);
 }
 
 static struct smp_chan *smp_chan_create(struct l2cap_conn *conn)
@@ -379,7 +389,15 @@ static struct smp_chan *smp_chan_create(struct l2cap_conn *conn)
 
 void smp_chan_destroy(struct l2cap_conn *conn)
 {
-       kfree(conn->smp_chan);
+       struct smp_chan *smp = conn->smp_chan;
+
+       clear_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->pend);
+
+       if (smp->tfm)
+               crypto_free_blkcipher(smp->tfm);
+
+       kfree(smp);
+       conn->smp_chan = NULL;
        hci_conn_put(conn->hcon);
 }
 
@@ -647,6 +665,7 @@ int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb)
                break;
 
        case SMP_CMD_PAIRING_FAIL:
+               smp_failure(conn, skb->data[0], 0);
                reason = 0;
                err = -EPERM;
                break;
@@ -692,8 +711,7 @@ int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb)
 
 done:
        if (reason)
-               smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, sizeof(reason),
-                                                               &reason);
+               smp_failure(conn, reason, 1);
 
        kfree_skb(skb);
        return err;