Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/padovan/bluetoot...
authorJohn W. Linville <linville@tuxdriver.com>
Wed, 22 Jun 2011 20:06:58 +0000 (16:06 -0400)
committerJohn W. Linville <linville@tuxdriver.com>
Wed, 22 Jun 2011 20:06:58 +0000 (16:06 -0400)
15 files changed:
include/net/bluetooth/hci.h
include/net/bluetooth/hci_core.h
include/net/bluetooth/l2cap.h
include/net/bluetooth/mgmt.h
include/net/bluetooth/smp.h
net/bluetooth/Kconfig
net/bluetooth/Makefile
net/bluetooth/hci_conn.c
net/bluetooth/hci_core.c
net/bluetooth/hci_event.c
net/bluetooth/hci_sock.c
net/bluetooth/l2cap_core.c
net/bluetooth/l2cap_sock.c
net/bluetooth/mgmt.c
net/bluetooth/smp.c [new file with mode: 0644]

index bd285c6a5509785aa3ee97ae9889cd4919058ec3..65345cd215beca8b366292ec951bed0982e004ef 100644 (file)
@@ -745,6 +745,33 @@ struct hci_cp_le_conn_update {
        __le16   max_ce_len;
 } __packed;
 
+#define HCI_OP_LE_START_ENC            0x2019
+struct hci_cp_le_start_enc {
+       __le16  handle;
+       __u8    rand[8];
+       __le16  ediv;
+       __u8    ltk[16];
+} __packed;
+
+#define HCI_OP_LE_LTK_REPLY            0x201a
+struct hci_cp_le_ltk_reply {
+       __le16  handle;
+       __u8    ltk[16];
+} __packed;
+struct hci_rp_le_ltk_reply {
+       __u8    status;
+       __le16  handle;
+} __packed;
+
+#define HCI_OP_LE_LTK_NEG_REPLY                0x201b
+struct hci_cp_le_ltk_neg_reply {
+       __le16  handle;
+} __packed;
+struct hci_rp_le_ltk_neg_reply {
+       __u8    status;
+       __le16  handle;
+} __packed;
+
 /* ---- HCI Events ---- */
 #define HCI_EV_INQUIRY_COMPLETE                0x01
 
@@ -1035,6 +1062,13 @@ struct hci_ev_le_conn_complete {
        __u8     clk_accurancy;
 } __packed;
 
+#define HCI_EV_LE_LTK_REQ              0x05
+struct hci_ev_le_ltk_req {
+       __le16  handle;
+       __u8    random[8];
+       __le16  ediv;
+} __packed;
+
 /* Advertising report event types */
 #define ADV_IND                0x00
 #define ADV_DIRECT_IND 0x01
index 836d3e8c4bf10b46679f531197ad4e1192926acf..26233d4d371c2681c67a1168130671125441e1c1 100644 (file)
@@ -177,6 +177,8 @@ struct hci_dev {
 
        __u16                   init_last_cmd;
 
+       struct crypto_blkcipher *tfm;
+
        struct inquiry_cache    inq_cache;
        struct hci_conn_hash    conn_hash;
        struct list_head        blacklist;
@@ -247,6 +249,7 @@ struct hci_conn {
        __u8            power_save;
        __u16           disc_timeout;
        unsigned long   pend;
+       __u8            ltk[16];
 
        __u8            remote_cap;
        __u8            remote_oob;
@@ -526,6 +529,8 @@ int hci_inquiry(void __user *arg);
 
 struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev, bdaddr_t *bdaddr);
 int hci_blacklist_clear(struct hci_dev *hdev);
+int hci_blacklist_add(struct hci_dev *hdev, bdaddr_t *bdaddr);
+int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr);
 
 int hci_uuids_clear(struct hci_dev *hdev);
 
@@ -742,6 +747,9 @@ static inline void hci_encrypt_cfm(struct hci_conn *conn, __u8 status,
        if (conn->sec_level == BT_SECURITY_SDP)
                conn->sec_level = BT_SECURITY_LOW;
 
+       if (conn->pending_sec_level > conn->sec_level)
+               conn->sec_level = conn->pending_sec_level;
+
        hci_proto_encrypt_cfm(conn, status, encrypt);
 
        read_lock_bh(&hci_cb_list_lock);
@@ -859,4 +867,9 @@ void hci_req_complete(struct hci_dev *hdev, __u16 cmd, int result);
 
 void hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max,
                                        u16 latency, u16 to_multiplier);
+void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __u8 rand[8],
+                                                       __u8 ltk[16]);
+void hci_le_ltk_reply(struct hci_conn *conn, u8 ltk[16]);
+void hci_le_ltk_neg_reply(struct hci_conn *conn);
+
 #endif /* __HCI_CORE_H */
index 0529d278e068932284babf880b62777fdadf4a5b..9c18e555b6ed0c302552f2676f36a04f7ddc5389 100644 (file)
@@ -287,6 +287,10 @@ struct l2cap_chan {
 
        struct l2cap_conn       *conn;
 
+       __u8            state;
+
+       atomic_t        refcnt;
+
        __le16          psm;
        __u16           dcid;
        __u16           scid;
@@ -320,8 +324,8 @@ struct l2cap_chan {
        __u16           monitor_timeout;
        __u16           mps;
 
-       __u8            conf_state;
-       __u16           conn_state;
+       unsigned long   conf_state;
+       unsigned long   conn_state;
 
        __u8            next_tx_seq;
        __u8            expected_ack_seq;
@@ -354,6 +358,18 @@ struct l2cap_chan {
 
        struct list_head list;
        struct list_head global_l;
+
+       void            *data;
+       struct l2cap_ops *ops;
+};
+
+struct l2cap_ops {
+       char            *name;
+
+       struct l2cap_chan       *(*new_connection) (void *data);
+       int                     (*recv) (void *data, struct sk_buff *skb);
+       void                    (*close) (void *data);
+       void                    (*state_change) (void *data, int state);
 };
 
 struct l2cap_conn {
@@ -379,6 +395,15 @@ struct l2cap_conn {
 
        __u8            disc_reason;
 
+       __u8            preq[7]; /* SMP Pairing Request */
+       __u8            prsp[7]; /* SMP Pairing Response */
+       __u8            prnd[16]; /* SMP Pairing Random */
+       __u8            pcnf[16]; /* SMP Pairing Confirm */
+       __u8            tk[16]; /* SMP Temporary Key */
+       __u8            smp_key_size;
+
+       struct timer_list security_timer;
+
        struct list_head chan_l;
        rwlock_t        chan_lock;
 };
@@ -399,36 +424,45 @@ struct l2cap_pinfo {
        struct l2cap_chan       *chan;
 };
 
-#define L2CAP_CONF_REQ_SENT       0x01
-#define L2CAP_CONF_INPUT_DONE     0x02
-#define L2CAP_CONF_OUTPUT_DONE    0x04
-#define L2CAP_CONF_MTU_DONE       0x08
-#define L2CAP_CONF_MODE_DONE      0x10
-#define L2CAP_CONF_CONNECT_PEND   0x20
-#define L2CAP_CONF_NO_FCS_RECV    0x40
-#define L2CAP_CONF_STATE2_DEVICE  0x80
+enum {
+       CONF_REQ_SENT,
+       CONF_INPUT_DONE,
+       CONF_OUTPUT_DONE,
+       CONF_MTU_DONE,
+       CONF_MODE_DONE,
+       CONF_CONNECT_PEND,
+       CONF_NO_FCS_RECV,
+       CONF_STATE2_DEVICE,
+};
 
 #define L2CAP_CONF_MAX_CONF_REQ 2
 #define L2CAP_CONF_MAX_CONF_RSP 2
 
-#define L2CAP_CONN_SAR_SDU         0x0001
-#define L2CAP_CONN_SREJ_SENT       0x0002
-#define L2CAP_CONN_WAIT_F          0x0004
-#define L2CAP_CONN_SREJ_ACT        0x0008
-#define L2CAP_CONN_SEND_PBIT       0x0010
-#define L2CAP_CONN_REMOTE_BUSY     0x0020
-#define L2CAP_CONN_LOCAL_BUSY      0x0040
-#define L2CAP_CONN_REJ_ACT         0x0080
-#define L2CAP_CONN_SEND_FBIT       0x0100
-#define L2CAP_CONN_RNR_SENT        0x0200
-#define L2CAP_CONN_SAR_RETRY       0x0400
-
-#define __mod_retrans_timer() mod_timer(&chan->retrans_timer, \
-               jiffies +  msecs_to_jiffies(L2CAP_DEFAULT_RETRANS_TO));
-#define __mod_monitor_timer() mod_timer(&chan->monitor_timer, \
-               jiffies + msecs_to_jiffies(L2CAP_DEFAULT_MONITOR_TO));
-#define __mod_ack_timer() mod_timer(&chan->ack_timer, \
-               jiffies + msecs_to_jiffies(L2CAP_DEFAULT_ACK_TO));
+enum {
+       CONN_SAR_SDU,
+       CONN_SREJ_SENT,
+       CONN_WAIT_F,
+       CONN_SREJ_ACT,
+       CONN_SEND_PBIT,
+       CONN_REMOTE_BUSY,
+       CONN_LOCAL_BUSY,
+       CONN_REJ_ACT,
+       CONN_SEND_FBIT,
+       CONN_RNR_SENT,
+       CONN_SAR_RETRY,
+};
+
+#define __set_chan_timer(c, t) l2cap_set_timer(c, &c->chan_timer, (t))
+#define __clear_chan_timer(c) l2cap_clear_timer(c, &c->chan_timer)
+#define __set_retrans_timer(c) l2cap_set_timer(c, &c->retrans_timer, \
+               L2CAP_DEFAULT_RETRANS_TO);
+#define __clear_retrans_timer(c) l2cap_clear_timer(c, &c->retrans_timer)
+#define __set_monitor_timer(c) l2cap_set_timer(c, &c->monitor_timer, \
+               L2CAP_DEFAULT_MONITOR_TO);
+#define __clear_monitor_timer(c) l2cap_clear_timer(c, &c->monitor_timer)
+#define __set_ack_timer(c) l2cap_set_timer(c, &chan->ack_timer, \
+               L2CAP_DEFAULT_ACK_TO);
+#define __clear_ack_timer(c) l2cap_clear_timer(c, &c->ack_timer)
 
 static inline int l2cap_tx_window_full(struct l2cap_chan *ch)
 {
@@ -459,11 +493,6 @@ int __l2cap_wait_ack(struct sock *sk);
 int l2cap_add_psm(struct l2cap_chan *chan, bdaddr_t *src, __le16 psm);
 int l2cap_add_scid(struct l2cap_chan *chan,  __u16 scid);
 
-void l2cap_sock_kill(struct sock *sk);
-void l2cap_sock_init(struct sock *sk, struct sock *parent);
-struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock,
-                                                       int proto, gfp_t prio);
-
 struct l2cap_chan *l2cap_chan_create(struct sock *sk);
 void l2cap_chan_close(struct l2cap_chan *chan, int reason);
 void l2cap_chan_destroy(struct l2cap_chan *chan);
index 4899286ed4e4a4ff9e20180675d618de8e0f7d62..45bea25d737f5bac5757d1ae6435c9054ba55114 100644 (file)
@@ -199,6 +199,16 @@ struct mgmt_cp_remove_remote_oob_data {
 
 #define MGMT_OP_STOP_DISCOVERY         0x001C
 
+#define MGMT_OP_BLOCK_DEVICE           0x001D
+struct mgmt_cp_block_device {
+       bdaddr_t bdaddr;
+} __packed;
+
+#define MGMT_OP_UNBLOCK_DEVICE         0x001E
+struct mgmt_cp_unblock_device {
+       bdaddr_t bdaddr;
+} __packed;
+
 #define MGMT_EV_CMD_COMPLETE           0x0001
 struct mgmt_ev_cmd_complete {
        __le16 opcode;
index 8f2edbf979dceb9f5461074fd18e45382dc6d273..4fb7d198a87642cd862e0e88b3c3cf375d0ce3d3 100644 (file)
@@ -1,3 +1,25 @@
+/*
+   BlueZ - Bluetooth protocol stack for Linux
+   Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License version 2 as
+   published by the Free Software Foundation;
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
+   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
+   SOFTWARE IS DISCLAIMED.
+*/
+
 #ifndef __SMP_H
 #define __SMP_H
 
@@ -16,6 +38,23 @@ struct smp_cmd_pairing {
        __u8    resp_key_dist;
 } __packed;
 
+#define SMP_IO_DISPLAY_ONLY    0x00
+#define SMP_IO_DISPLAY_YESNO   0x01
+#define SMP_IO_KEYBOARD_ONLY   0x02
+#define SMP_IO_NO_INPUT_OUTPUT 0x03
+#define SMP_IO_KEYBOARD_DISPLAY        0x04
+
+#define SMP_OOB_NOT_PRESENT    0x00
+#define SMP_OOB_PRESENT                0x01
+
+#define SMP_DIST_ENC_KEY       0x01
+#define SMP_DIST_ID_KEY                0x02
+#define SMP_DIST_SIGN          0x04
+
+#define SMP_AUTH_NONE          0x00
+#define SMP_AUTH_BONDING       0x01
+#define SMP_AUTH_MITM          0x04
+
 #define SMP_CMD_PAIRING_CONFIRM        0x03
 struct smp_cmd_pairing_confirm {
        __u8    confirm_val[16];
@@ -73,4 +112,11 @@ struct smp_cmd_security_req {
 #define SMP_UNSPECIFIED                0x08
 #define SMP_REPEATED_ATTEMPTS          0x09
 
+#define SMP_MIN_ENC_KEY_SIZE           7
+#define SMP_MAX_ENC_KEY_SIZE           16
+
+/* SMP Commands */
+int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level);
+int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb);
+
 #endif /* __SMP_H */
index 6ae5ec50858744daed011d49041e89030e921d60..f495dea741e3eab06ce2ae73817dfca7e7a724f7 100644 (file)
@@ -22,6 +22,7 @@ menuconfig BT
             BNEP Module (Bluetooth Network Encapsulation Protocol)
             CMTP Module (CAPI Message Transport Protocol)
             HIDP Module (Human Interface Device Protocol)
+            SMP Module (Security Manager Protocol)
 
          Say Y here to compile Bluetooth support into the kernel or say M to
          compile it as module (bluetooth).
@@ -36,11 +37,18 @@ if BT != n
 config BT_L2CAP
        bool "L2CAP protocol support"
        select CRC16
+       select CRYPTO
+       select CRYPTO_BLKCIPHER
+       select CRYPTO_AES
+       select CRYPTO_ECB
        help
          L2CAP (Logical Link Control and Adaptation Protocol) provides
          connection oriented and connection-less data transport.  L2CAP
          support is required for most Bluetooth applications.
 
+         Also included is support for SMP (Security Manager Protocol) which
+         is the security layer on top of LE (Low Energy) links.
+
 config BT_SCO
        bool "SCO links support"
        help
index f04fe9a9d6342bbded032e652c80115d998ab959..9b67f3d08fa4750296645ec2b06c89dd1ff61ed0 100644 (file)
@@ -9,5 +9,5 @@ obj-$(CONFIG_BT_CMTP)   += cmtp/
 obj-$(CONFIG_BT_HIDP)  += hidp/
 
 bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o hci_sock.o hci_sysfs.o lib.o
-bluetooth-$(CONFIG_BT_L2CAP)   += l2cap_core.o l2cap_sock.o
+bluetooth-$(CONFIG_BT_L2CAP)   += l2cap_core.o l2cap_sock.o smp.o
 bluetooth-$(CONFIG_BT_SCO)     += sco.o
index 37f5a174f072eac0548451d94db1c45a7a34ca32..fa48c0b3d93c1d2f5c83b4cab3ec99ea7f41b1e1 100644 (file)
@@ -53,6 +53,7 @@ static void hci_le_connect(struct hci_conn *conn)
        conn->state = BT_CONNECT;
        conn->out = 1;
        conn->link_mode |= HCI_LM_MASTER;
+       conn->sec_level = BT_SECURITY_LOW;
 
        memset(&cp, 0, sizeof(cp));
        cp.scan_interval = cpu_to_le16(0x0004);
@@ -204,6 +205,55 @@ void hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max,
 }
 EXPORT_SYMBOL(hci_le_conn_update);
 
+void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __u8 rand[8],
+                                                       __u8 ltk[16])
+{
+       struct hci_dev *hdev = conn->hdev;
+       struct hci_cp_le_start_enc cp;
+
+       BT_DBG("%p", conn);
+
+       memset(&cp, 0, sizeof(cp));
+
+       cp.handle = cpu_to_le16(conn->handle);
+       memcpy(cp.ltk, ltk, sizeof(cp.ltk));
+       cp.ediv = ediv;
+       memcpy(cp.rand, rand, sizeof(rand));
+
+       hci_send_cmd(hdev, HCI_OP_LE_START_ENC, sizeof(cp), &cp);
+}
+EXPORT_SYMBOL(hci_le_start_enc);
+
+void hci_le_ltk_reply(struct hci_conn *conn, u8 ltk[16])
+{
+       struct hci_dev *hdev = conn->hdev;
+       struct hci_cp_le_ltk_reply cp;
+
+       BT_DBG("%p", conn);
+
+       memset(&cp, 0, sizeof(cp));
+
+       cp.handle = cpu_to_le16(conn->handle);
+       memcpy(cp.ltk, ltk, sizeof(ltk));
+
+       hci_send_cmd(hdev, HCI_OP_LE_LTK_REPLY, sizeof(cp), &cp);
+}
+EXPORT_SYMBOL(hci_le_ltk_reply);
+
+void hci_le_ltk_neg_reply(struct hci_conn *conn)
+{
+       struct hci_dev *hdev = conn->hdev;
+       struct hci_cp_le_ltk_neg_reply cp;
+
+       BT_DBG("%p", conn);
+
+       memset(&cp, 0, sizeof(cp));
+
+       cp.handle = cpu_to_le16(conn->handle);
+
+       hci_send_cmd(hdev, HCI_OP_LE_LTK_NEG_REPLY, sizeof(cp), &cp);
+}
+
 /* Device _must_ be locked */
 void hci_sco_setup(struct hci_conn *conn, __u8 status)
 {
@@ -620,11 +670,11 @@ int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type)
                goto encrypt;
 
 auth:
-       if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend))
+       if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend))
                return 0;
 
-       hci_conn_auth(conn, sec_level, auth_type);
-       return 0;
+       if (!hci_conn_auth(conn, sec_level, auth_type))
+               return 0;
 
 encrypt:
        if (conn->link_mode & HCI_LM_ENCRYPT)
index e14e8a1cb04e68a0a52ae0616baf41525bbbae7b..0029e178e52e80202cb4bf35346116d75e64eecc 100644 (file)
@@ -42,6 +42,7 @@
 #include <linux/notifier.h>
 #include <linux/rfkill.h>
 #include <linux/timer.h>
+#include <linux/crypto.h>
 #include <net/sock.h>
 
 #include <asm/system.h>
@@ -59,6 +60,8 @@ static void hci_tx_task(unsigned long arg);
 
 static DEFINE_RWLOCK(hci_task_lock);
 
+static int enable_smp;
+
 /* HCI device list */
 LIST_HEAD(hci_dev_list);
 DEFINE_RWLOCK(hci_dev_list_lock);
@@ -1202,6 +1205,97 @@ int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *hash,
        return 0;
 }
 
+struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev,
+                                               bdaddr_t *bdaddr)
+{
+       struct list_head *p;
+
+       list_for_each(p, &hdev->blacklist) {
+               struct bdaddr_list *b;
+
+               b = list_entry(p, struct bdaddr_list, list);
+
+               if (bacmp(bdaddr, &b->bdaddr) == 0)
+                       return b;
+       }
+
+       return NULL;
+}
+
+int hci_blacklist_clear(struct hci_dev *hdev)
+{
+       struct list_head *p, *n;
+
+       list_for_each_safe(p, n, &hdev->blacklist) {
+               struct bdaddr_list *b;
+
+               b = list_entry(p, struct bdaddr_list, list);
+
+               list_del(p);
+               kfree(b);
+       }
+
+       return 0;
+}
+
+int hci_blacklist_add(struct hci_dev *hdev, bdaddr_t *bdaddr)
+{
+       struct bdaddr_list *entry;
+       int err;
+
+       if (bacmp(bdaddr, BDADDR_ANY) == 0)
+               return -EBADF;
+
+       hci_dev_lock(hdev);
+
+       if (hci_blacklist_lookup(hdev, bdaddr)) {
+               err = -EEXIST;
+               goto err;
+       }
+
+       entry = kzalloc(sizeof(struct bdaddr_list), GFP_KERNEL);
+       if (!entry) {
+               return -ENOMEM;
+               goto err;
+       }
+
+       bacpy(&entry->bdaddr, bdaddr);
+
+       list_add(&entry->list, &hdev->blacklist);
+
+       err = 0;
+
+err:
+       hci_dev_unlock(hdev);
+       return err;
+}
+
+int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr)
+{
+       struct bdaddr_list *entry;
+       int err = 0;
+
+       hci_dev_lock(hdev);
+
+       if (bacmp(bdaddr, BDADDR_ANY) == 0) {
+               hci_blacklist_clear(hdev);
+               goto done;
+       }
+
+       entry = hci_blacklist_lookup(hdev, bdaddr);
+       if (!entry) {
+               err = -ENOENT;
+               goto done;
+       }
+
+       list_del(&entry->list);
+       kfree(entry);
+
+done:
+       hci_dev_unlock(hdev);
+       return err;
+}
+
 static void hci_clear_adv_cache(unsigned long arg)
 {
        struct hci_dev *hdev = (void *) arg;
@@ -1274,6 +1368,14 @@ int hci_add_adv_entry(struct hci_dev *hdev,
        return 0;
 }
 
+static struct crypto_blkcipher *alloc_cypher(void)
+{
+       if (enable_smp)
+               return crypto_alloc_blkcipher("ecb(aes)", 0, CRYPTO_ALG_ASYNC);
+
+       return ERR_PTR(-ENOTSUPP);
+}
+
 /* Register HCI device */
 int hci_register_dev(struct hci_dev *hdev)
 {
@@ -1358,6 +1460,11 @@ int hci_register_dev(struct hci_dev *hdev)
        if (!hdev->workqueue)
                goto nomem;
 
+       hdev->tfm = alloc_cypher();
+       if (IS_ERR(hdev->tfm))
+               BT_INFO("Failed to load transform for ecb(aes): %ld",
+                                                       PTR_ERR(hdev->tfm));
+
        hci_register_sysfs(hdev);
 
        hdev->rfkill = rfkill_alloc(hdev->name, &hdev->dev,
@@ -1406,6 +1513,9 @@ int hci_unregister_dev(struct hci_dev *hdev)
                                        !test_bit(HCI_SETUP, &hdev->flags))
                mgmt_index_removed(hdev->id);
 
+       if (!IS_ERR(hdev->tfm))
+               crypto_free_blkcipher(hdev->tfm);
+
        hci_notify(hdev, HCI_DEV_UNREG);
 
        if (hdev->rfkill) {
@@ -2242,3 +2352,6 @@ static void hci_cmd_task(unsigned long arg)
                }
        }
 }
+
+module_param(enable_smp, bool, 0644);
+MODULE_PARM_DESC(enable_smp, "Enable SMP support (LE only)");
index 0f643f84131f8e545ae47d38b386827a1705d3b7..ac2c5e89617ca15f4adc5c2902af282566035039 100644 (file)
@@ -868,6 +868,30 @@ static void hci_cc_le_set_scan_enable(struct hci_dev *hdev,
        hci_dev_unlock(hdev);
 }
 
+static void hci_cc_le_ltk_reply(struct hci_dev *hdev, struct sk_buff *skb)
+{
+       struct hci_rp_le_ltk_reply *rp = (void *) skb->data;
+
+       BT_DBG("%s status 0x%x", hdev->name, rp->status);
+
+       if (rp->status)
+               return;
+
+       hci_req_complete(hdev, HCI_OP_LE_LTK_REPLY, rp->status);
+}
+
+static void hci_cc_le_ltk_neg_reply(struct hci_dev *hdev, struct sk_buff *skb)
+{
+       struct hci_rp_le_ltk_neg_reply *rp = (void *) skb->data;
+
+       BT_DBG("%s status 0x%x", hdev->name, rp->status);
+
+       if (rp->status)
+               return;
+
+       hci_req_complete(hdev, HCI_OP_LE_LTK_NEG_REPLY, rp->status);
+}
+
 static inline void hci_cs_inquiry(struct hci_dev *hdev, __u8 status)
 {
        BT_DBG("%s status 0x%x", hdev->name, status);
@@ -1248,6 +1272,11 @@ static void hci_cs_le_create_conn(struct hci_dev *hdev, __u8 status)
        hci_dev_unlock(hdev);
 }
 
+static void hci_cs_le_start_enc(struct hci_dev *hdev, u8 status)
+{
+       BT_DBG("%s status 0x%x", hdev->name, status);
+}
+
 static inline void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
 {
        __u8 status = *((__u8 *) skb->data);
@@ -1593,6 +1622,7 @@ static inline void hci_encrypt_change_evt(struct hci_dev *hdev, struct sk_buff *
                                /* Encryption implies authentication */
                                conn->link_mode |= HCI_LM_AUTH;
                                conn->link_mode |= HCI_LM_ENCRYPT;
+                               conn->sec_level = conn->pending_sec_level;
                        } else
                                conn->link_mode &= ~HCI_LM_ENCRYPT;
                }
@@ -1856,6 +1886,14 @@ static inline void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *sk
                hci_cc_le_set_scan_enable(hdev, skb);
                break;
 
+       case HCI_OP_LE_LTK_REPLY:
+               hci_cc_le_ltk_reply(hdev, skb);
+               break;
+
+       case HCI_OP_LE_LTK_NEG_REPLY:
+               hci_cc_le_ltk_neg_reply(hdev, skb);
+               break;
+
        default:
                BT_DBG("%s opcode 0x%x", hdev->name, opcode);
                break;
@@ -1934,6 +1972,10 @@ static inline void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb)
                hci_cs_le_create_conn(hdev, ev->status);
                break;
 
+       case HCI_OP_LE_START_ENC:
+               hci_cs_le_start_enc(hdev, ev->status);
+               break;
+
        default:
                BT_DBG("%s opcode 0x%x", hdev->name, opcode);
                break;
@@ -2712,6 +2754,7 @@ static inline void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff
 
        mgmt_connected(hdev->id, &ev->bdaddr);
 
+       conn->sec_level = BT_SECURITY_LOW;
        conn->handle = __le16_to_cpu(ev->handle);
        conn->state = BT_CONNECTED;
 
@@ -2745,6 +2788,28 @@ static inline void hci_le_adv_report_evt(struct hci_dev *hdev,
        hci_dev_unlock(hdev);
 }
 
+static inline void hci_le_ltk_request_evt(struct hci_dev *hdev,
+                                               struct sk_buff *skb)
+{
+       struct hci_ev_le_ltk_req *ev = (void *) skb->data;
+       struct hci_cp_le_ltk_reply cp;
+       struct hci_conn *conn;
+
+       BT_DBG("%s handle %d", hdev->name, cpu_to_le16(ev->handle));
+
+       hci_dev_lock(hdev);
+
+       conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle));
+
+       memset(&cp, 0, sizeof(cp));
+       cp.handle = cpu_to_le16(conn->handle);
+       memcpy(cp.ltk, conn->ltk, sizeof(conn->ltk));
+
+       hci_send_cmd(hdev, HCI_OP_LE_LTK_REPLY, sizeof(cp), &cp);
+
+       hci_dev_unlock(hdev);
+}
+
 static inline void hci_le_meta_evt(struct hci_dev *hdev, struct sk_buff *skb)
 {
        struct hci_ev_le_meta *le_ev = (void *) skb->data;
@@ -2760,6 +2825,10 @@ static inline void hci_le_meta_evt(struct hci_dev *hdev, struct sk_buff *skb)
                hci_le_adv_report_evt(hdev, skb);
                break;
 
+       case HCI_EV_LE_LTK_REQ:
+               hci_le_ltk_request_evt(hdev, skb);
+               break;
+
        default:
                break;
        }
index 295e4a88fff8ccea7e197560d47f90bc6584b7df..ff02cf5e77ccdd576d2f42f78f2c4715e3a47d5e 100644 (file)
@@ -180,82 +180,24 @@ static int hci_sock_release(struct socket *sock)
        return 0;
 }
 
-struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev, bdaddr_t *bdaddr)
-{
-       struct list_head *p;
-
-       list_for_each(p, &hdev->blacklist) {
-               struct bdaddr_list *b;
-
-               b = list_entry(p, struct bdaddr_list, list);
-
-               if (bacmp(bdaddr, &b->bdaddr) == 0)
-                       return b;
-       }
-
-       return NULL;
-}
-
-static int hci_blacklist_add(struct hci_dev *hdev, void __user *arg)
+static int hci_sock_blacklist_add(struct hci_dev *hdev, void __user *arg)
 {
        bdaddr_t bdaddr;
-       struct bdaddr_list *entry;
 
        if (copy_from_user(&bdaddr, arg, sizeof(bdaddr)))
                return -EFAULT;
 
-       if (bacmp(&bdaddr, BDADDR_ANY) == 0)
-               return -EBADF;
-
-       if (hci_blacklist_lookup(hdev, &bdaddr))
-               return -EEXIST;
-
-       entry = kzalloc(sizeof(struct bdaddr_list), GFP_KERNEL);
-       if (!entry)
-               return -ENOMEM;
-
-       bacpy(&entry->bdaddr, &bdaddr);
-
-       list_add(&entry->list, &hdev->blacklist);
-
-       return 0;
-}
-
-int hci_blacklist_clear(struct hci_dev *hdev)
-{
-       struct list_head *p, *n;
-
-       list_for_each_safe(p, n, &hdev->blacklist) {
-               struct bdaddr_list *b;
-
-               b = list_entry(p, struct bdaddr_list, list);
-
-               list_del(p);
-               kfree(b);
-       }
-
-       return 0;
+       return hci_blacklist_add(hdev, &bdaddr);
 }
 
-static int hci_blacklist_del(struct hci_dev *hdev, void __user *arg)
+static int hci_sock_blacklist_del(struct hci_dev *hdev, void __user *arg)
 {
        bdaddr_t bdaddr;
-       struct bdaddr_list *entry;
 
        if (copy_from_user(&bdaddr, arg, sizeof(bdaddr)))
                return -EFAULT;
 
-       if (bacmp(&bdaddr, BDADDR_ANY) == 0)
-               return hci_blacklist_clear(hdev);
-
-       entry = hci_blacklist_lookup(hdev, &bdaddr);
-       if (!entry)
-               return -ENOENT;
-
-       list_del(&entry->list);
-       kfree(entry);
-
-       return 0;
+       return hci_blacklist_del(hdev, &bdaddr);
 }
 
 /* Ioctls that require bound socket */
@@ -290,12 +232,12 @@ static inline int hci_sock_bound_ioctl(struct sock *sk, unsigned int cmd, unsign
        case HCIBLOCKADDR:
                if (!capable(CAP_NET_ADMIN))
                        return -EACCES;
-               return hci_blacklist_add(hdev, (void __user *) arg);
+               return hci_sock_blacklist_add(hdev, (void __user *) arg);
 
        case HCIUNBLOCKADDR:
                if (!capable(CAP_NET_ADMIN))
                        return -EACCES;
-               return hci_blacklist_del(hdev, (void __user *) arg);
+               return hci_sock_blacklist_del(hdev, (void __user *) arg);
 
        default:
                if (hdev->ioctl)
index dff9d76fe790ebc18f7ade32a06ab138db54a36a..9ec9c8c5eb5ec637189cc2303cf332926061e85b 100644 (file)
@@ -54,6 +54,7 @@
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
 #include <net/bluetooth/l2cap.h>
+#include <net/bluetooth/smp.h>
 
 int disable_ertm;
 
@@ -78,6 +79,18 @@ static void l2cap_send_disconn_req(struct l2cap_conn *conn,
 static int l2cap_ertm_data_rcv(struct sock *sk, struct sk_buff *skb);
 
 /* ---- L2CAP channels ---- */
+
+static inline void chan_hold(struct l2cap_chan *c)
+{
+       atomic_inc(&c->refcnt);
+}
+
+static inline void chan_put(struct l2cap_chan *c)
+{
+       if (atomic_dec_and_test(&c->refcnt))
+               kfree(c);
+}
+
 static struct l2cap_chan *__l2cap_get_chan_by_dcid(struct l2cap_conn *conn, u16 cid)
 {
        struct l2cap_chan *c;
@@ -208,20 +221,26 @@ static u16 l2cap_alloc_cid(struct l2cap_conn *conn)
        return 0;
 }
 
-static void l2cap_chan_set_timer(struct l2cap_chan *chan, long timeout)
+static void l2cap_set_timer(struct l2cap_chan *chan, struct timer_list *timer, long timeout)
 {
-       BT_DBG("chan %p state %d timeout %ld", chan->sk, chan->sk->sk_state,
-                                                                timeout);
-       if (!mod_timer(&chan->chan_timer, jiffies + timeout))
-              sock_hold(chan->sk);
+       BT_DBG("chan %p state %d timeout %ld", chan->sk, chan->state, timeout);
+
+       if (!mod_timer(timer, jiffies + timeout))
+              chan_hold(chan);
 }
 
-static void l2cap_chan_clear_timer(struct l2cap_chan *chan)
+static void l2cap_clear_timer(struct l2cap_chan *chan, struct timer_list *timer)
 {
-       BT_DBG("chan %p state %d", chan, chan->sk->sk_state);
+       BT_DBG("chan %p state %d", chan, chan->state);
+
+       if (timer_pending(timer) && del_timer(timer))
+              chan_put(chan);
+}
 
-       if (timer_pending(&chan->chan_timer) && del_timer(&chan->chan_timer))
-              __sock_put(chan->sk);
+static void l2cap_state_change(struct l2cap_chan *chan, int state)
+{
+       chan->state = state;
+       chan->ops->state_change(chan->data, state);
 }
 
 static void l2cap_chan_timeout(unsigned long arg)
@@ -230,21 +249,21 @@ static void l2cap_chan_timeout(unsigned long arg)
        struct sock *sk = chan->sk;
        int reason;
 
-       BT_DBG("chan %p state %d", chan, sk->sk_state);
+       BT_DBG("chan %p state %d", chan, chan->state);
 
        bh_lock_sock(sk);
 
        if (sock_owned_by_user(sk)) {
                /* sk is owned by user. Try again later */
-               l2cap_chan_set_timer(chan, HZ / 5);
+               __set_chan_timer(chan, HZ / 5);
                bh_unlock_sock(sk);
-               sock_put(sk);
+               chan_put(chan);
                return;
        }
 
-       if (sk->sk_state == BT_CONNECTED || sk->sk_state == BT_CONFIG)
+       if (chan->state == BT_CONNECTED || chan->state == BT_CONFIG)
                reason = ECONNREFUSED;
-       else if (sk->sk_state == BT_CONNECT &&
+       else if (chan->state == BT_CONNECT &&
                                        chan->sec_level != BT_SECURITY_SDP)
                reason = ECONNREFUSED;
        else
@@ -254,8 +273,8 @@ static void l2cap_chan_timeout(unsigned long arg)
 
        bh_unlock_sock(sk);
 
-       l2cap_sock_kill(sk);
-       sock_put(sk);
+       chan->ops->close(chan->data);
+       chan_put(chan);
 }
 
 struct l2cap_chan *l2cap_chan_create(struct sock *sk)
@@ -274,6 +293,10 @@ struct l2cap_chan *l2cap_chan_create(struct sock *sk)
 
        setup_timer(&chan->chan_timer, l2cap_chan_timeout, (unsigned long) chan);
 
+       chan->state = BT_OPEN;
+
+       atomic_set(&chan->refcnt, 1);
+
        return chan;
 }
 
@@ -283,13 +306,11 @@ void l2cap_chan_destroy(struct l2cap_chan *chan)
        list_del(&chan->global_l);
        write_unlock_bh(&chan_list_lock);
 
-       kfree(chan);
+       chan_put(chan);
 }
 
 static void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
 {
-       struct sock *sk = chan->sk;
-
        BT_DBG("conn %p, psm 0x%2.2x, dcid 0x%4.4x", conn,
                        chan->psm, chan->dcid);
 
@@ -320,7 +341,7 @@ static void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
                chan->omtu = L2CAP_DEFAULT_MTU;
        }
 
-       sock_hold(sk);
+       chan_hold(chan);
 
        list_add(&chan->list, &conn->chan_l);
 }
@@ -333,7 +354,7 @@ static void l2cap_chan_del(struct l2cap_chan *chan, int err)
        struct l2cap_conn *conn = chan->conn;
        struct sock *parent = bt_sk(sk)->parent;
 
-       l2cap_chan_clear_timer(chan);
+       __clear_chan_timer(chan);
 
        BT_DBG("chan %p, conn %p, err %d", chan, conn, err);
 
@@ -342,13 +363,13 @@ static void l2cap_chan_del(struct l2cap_chan *chan, int err)
                write_lock_bh(&conn->chan_lock);
                list_del(&chan->list);
                write_unlock_bh(&conn->chan_lock);
-               __sock_put(sk);
+               chan_put(chan);
 
                chan->conn = NULL;
                hci_conn_put(conn->hcon);
        }
 
-       sk->sk_state = BT_CLOSED;
+       l2cap_state_change(chan, BT_CLOSED);
        sock_set_flag(sk, SOCK_ZAPPED);
 
        if (err)
@@ -360,8 +381,8 @@ static void l2cap_chan_del(struct l2cap_chan *chan, int err)
        } else
                sk->sk_state_change(sk);
 
-       if (!(chan->conf_state & L2CAP_CONF_OUTPUT_DONE &&
-                       chan->conf_state & L2CAP_CONF_INPUT_DONE))
+       if (!(test_bit(CONF_OUTPUT_DONE, &chan->conf_state) &&
+                       test_bit(CONF_INPUT_DONE, &chan->conf_state)))
                return;
 
        skb_queue_purge(&chan->tx_q);
@@ -369,9 +390,9 @@ static void l2cap_chan_del(struct l2cap_chan *chan, int err)
        if (chan->mode == L2CAP_MODE_ERTM) {
                struct srej_list *l, *tmp;
 
-               del_timer(&chan->retrans_timer);
-               del_timer(&chan->monitor_timer);
-               del_timer(&chan->ack_timer);
+               __clear_retrans_timer(chan);
+               __clear_monitor_timer(chan);
+               __clear_ack_timer(chan);
 
                skb_queue_purge(&chan->srej_q);
                skb_queue_purge(&chan->busy_q);
@@ -391,15 +412,13 @@ static void l2cap_chan_cleanup_listen(struct sock *parent)
 
        /* Close not yet accepted channels */
        while ((sk = bt_accept_dequeue(parent, NULL))) {
-               l2cap_chan_clear_timer(l2cap_pi(sk)->chan);
+               struct l2cap_chan *chan = l2cap_pi(sk)->chan;
+               __clear_chan_timer(chan);
                lock_sock(sk);
-               l2cap_chan_close(l2cap_pi(sk)->chan, ECONNRESET);
+               l2cap_chan_close(chan, ECONNRESET);
                release_sock(sk);
-               l2cap_sock_kill(sk);
+               chan->ops->close(chan->data);
        }
-
-       parent->sk_state = BT_CLOSED;
-       sock_set_flag(parent, SOCK_ZAPPED);
 }
 
 void l2cap_chan_close(struct l2cap_chan *chan, int reason)
@@ -407,19 +426,22 @@ void l2cap_chan_close(struct l2cap_chan *chan, int reason)
        struct l2cap_conn *conn = chan->conn;
        struct sock *sk = chan->sk;
 
-       BT_DBG("chan %p state %d socket %p", chan, sk->sk_state, sk->sk_socket);
+       BT_DBG("chan %p state %d socket %p", chan, chan->state, sk->sk_socket);
 
-       switch (sk->sk_state) {
+       switch (chan->state) {
        case BT_LISTEN:
                l2cap_chan_cleanup_listen(sk);
+
+               l2cap_state_change(chan, BT_CLOSED);
+               sock_set_flag(sk, SOCK_ZAPPED);
                break;
 
        case BT_CONNECTED:
        case BT_CONFIG:
                if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED &&
                                        conn->hcon->type == ACL_LINK) {
-                       l2cap_chan_clear_timer(chan);
-                       l2cap_chan_set_timer(chan, sk->sk_sndtimeo);
+                       __clear_chan_timer(chan);
+                       __set_chan_timer(chan, sk->sk_sndtimeo);
                        l2cap_send_disconn_req(conn, chan, reason);
                } else
                        l2cap_chan_del(chan, reason);
@@ -435,7 +457,7 @@ void l2cap_chan_close(struct l2cap_chan *chan, int reason)
                                result = L2CAP_CR_SEC_BLOCK;
                        else
                                result = L2CAP_CR_BAD_PSM;
-                       sk->sk_state = BT_DISCONN;
+                       l2cap_state_change(chan, BT_DISCONN);
 
                        rsp.scid   = cpu_to_le16(chan->dcid);
                        rsp.dcid   = cpu_to_le16(chan->scid);
@@ -547,13 +569,11 @@ static inline void l2cap_send_sframe(struct l2cap_chan *chan, u16 control)
 {
        struct sk_buff *skb;
        struct l2cap_hdr *lh;
-       struct l2cap_pinfo *pi = l2cap_pi(chan->sk);
        struct l2cap_conn *conn = chan->conn;
-       struct sock *sk = (struct sock *)pi;
        int count, hlen = L2CAP_HDR_SIZE + 2;
        u8 flags;
 
-       if (sk->sk_state != BT_CONNECTED)
+       if (chan->state != BT_CONNECTED)
                return;
 
        if (chan->fcs == L2CAP_FCS_CRC16)
@@ -564,15 +584,11 @@ static inline void l2cap_send_sframe(struct l2cap_chan *chan, u16 control)
        count = min_t(unsigned int, conn->mtu, hlen);
        control |= L2CAP_CTRL_FRAME_TYPE;
 
-       if (chan->conn_state & L2CAP_CONN_SEND_FBIT) {
+       if (test_and_clear_bit(CONN_SEND_FBIT, &chan->conn_state))
                control |= L2CAP_CTRL_FINAL;
-               chan->conn_state &= ~L2CAP_CONN_SEND_FBIT;
-       }
 
-       if (chan->conn_state & L2CAP_CONN_SEND_PBIT) {
+       if (test_and_clear_bit(CONN_SEND_PBIT, &chan->conn_state))
                control |= L2CAP_CTRL_POLL;
-               chan->conn_state &= ~L2CAP_CONN_SEND_PBIT;
-       }
 
        skb = bt_skb_alloc(count, GFP_ATOMIC);
        if (!skb)
@@ -600,9 +616,9 @@ static inline void l2cap_send_sframe(struct l2cap_chan *chan, u16 control)
 
 static inline void l2cap_send_rr_or_rnr(struct l2cap_chan *chan, u16 control)
 {
-       if (chan->conn_state & L2CAP_CONN_LOCAL_BUSY) {
+       if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) {
                control |= L2CAP_SUPER_RCV_NOT_READY;
-               chan->conn_state |= L2CAP_CONN_RNR_SENT;
+               set_bit(CONN_RNR_SENT, &chan->conn_state);
        } else
                control |= L2CAP_SUPER_RCV_READY;
 
@@ -613,7 +629,7 @@ static inline void l2cap_send_rr_or_rnr(struct l2cap_chan *chan, u16 control)
 
 static inline int __l2cap_no_conn_pending(struct l2cap_chan *chan)
 {
-       return !(chan->conf_state & L2CAP_CONF_CONNECT_PEND);
+       return !test_bit(CONF_CONNECT_PEND, &chan->conf_state);
 }
 
 static void l2cap_do_start(struct l2cap_chan *chan)
@@ -631,7 +647,7 @@ static void l2cap_do_start(struct l2cap_chan *chan)
                        req.psm  = chan->psm;
 
                        chan->ident = l2cap_get_ident(conn);
-                       chan->conf_state |= L2CAP_CONF_CONNECT_PEND;
+                       set_bit(CONF_CONNECT_PEND, &chan->conf_state);
 
                        l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_REQ,
                                                        sizeof(req), &req);
@@ -678,9 +694,9 @@ static void l2cap_send_disconn_req(struct l2cap_conn *conn, struct l2cap_chan *c
        sk = chan->sk;
 
        if (chan->mode == L2CAP_MODE_ERTM) {
-               del_timer(&chan->retrans_timer);
-               del_timer(&chan->monitor_timer);
-               del_timer(&chan->ack_timer);
+               __clear_retrans_timer(chan);
+               __clear_monitor_timer(chan);
+               __clear_ack_timer(chan);
        }
 
        req.dcid = cpu_to_le16(chan->dcid);
@@ -688,7 +704,7 @@ static void l2cap_send_disconn_req(struct l2cap_conn *conn, struct l2cap_chan *c
        l2cap_send_cmd(conn, l2cap_get_ident(conn),
                        L2CAP_DISCONN_REQ, sizeof(req), &req);
 
-       sk->sk_state = BT_DISCONN;
+       l2cap_state_change(chan, BT_DISCONN);
        sk->sk_err = err;
 }
 
@@ -711,7 +727,7 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
                        continue;
                }
 
-               if (sk->sk_state == BT_CONNECT) {
+               if (chan->state == BT_CONNECT) {
                        struct l2cap_conn_req req;
 
                        if (!l2cap_check_security(chan) ||
@@ -720,14 +736,13 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
                                continue;
                        }
 
-                       if (!l2cap_mode_supported(chan->mode,
-                                       conn->feat_mask)
-                                       && chan->conf_state &
-                                       L2CAP_CONF_STATE2_DEVICE) {
+                       if (!l2cap_mode_supported(chan->mode, conn->feat_mask)
+                                       && test_bit(CONF_STATE2_DEVICE,
+                                       &chan->conf_state)) {
                                /* l2cap_chan_close() calls list_del(chan)
                                 * so release the lock */
                                read_unlock_bh(&conn->chan_lock);
-                                l2cap_chan_close(chan, ECONNRESET);
+                               l2cap_chan_close(chan, ECONNRESET);
                                read_lock_bh(&conn->chan_lock);
                                bh_unlock_sock(sk);
                                continue;
@@ -737,12 +752,12 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
                        req.psm  = chan->psm;
 
                        chan->ident = l2cap_get_ident(conn);
-                       chan->conf_state |= L2CAP_CONF_CONNECT_PEND;
+                       set_bit(CONF_CONNECT_PEND, &chan->conf_state);
 
                        l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_REQ,
                                                        sizeof(req), &req);
 
-               } else if (sk->sk_state == BT_CONNECT2) {
+               } else if (chan->state == BT_CONNECT2) {
                        struct l2cap_conn_rsp rsp;
                        char buf[128];
                        rsp.scid = cpu_to_le16(chan->dcid);
@@ -756,7 +771,7 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
                                        parent->sk_data_ready(parent, 0);
 
                                } else {
-                                       sk->sk_state = BT_CONFIG;
+                                       l2cap_state_change(chan, BT_CONFIG);
                                        rsp.result = cpu_to_le16(L2CAP_CR_SUCCESS);
                                        rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO);
                                }
@@ -768,13 +783,13 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
                        l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_RSP,
                                                        sizeof(rsp), &rsp);
 
-                       if (chan->conf_state & L2CAP_CONF_REQ_SENT ||
+                       if (test_bit(CONF_REQ_SENT, &chan->conf_state) ||
                                        rsp.result != L2CAP_CR_SUCCESS) {
                                bh_unlock_sock(sk);
                                continue;
                        }
 
-                       chan->conf_state |= L2CAP_CONF_REQ_SENT;
+                       set_bit(CONF_REQ_SENT, &chan->conf_state);
                        l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ,
                                                l2cap_build_conf_req(chan, buf), buf);
                        chan->num_conf_req++;
@@ -798,7 +813,7 @@ static struct l2cap_chan *l2cap_global_chan_by_scid(int state, __le16 cid, bdadd
        list_for_each_entry(c, &chan_list, global_l) {
                struct sock *sk = c->sk;
 
-               if (state && sk->sk_state != state)
+               if (state && c->state != state)
                        continue;
 
                if (c->scid == cid) {
@@ -842,24 +857,16 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn)
                goto clean;
        }
 
-       sk = l2cap_sock_alloc(sock_net(parent), NULL, BTPROTO_L2CAP, GFP_ATOMIC);
-       if (!sk)
-               goto clean;
-
-       chan = l2cap_chan_create(sk);
-       if (!chan) {
-               l2cap_sock_kill(sk);
+       chan = pchan->ops->new_connection(pchan->data);
+       if (!chan)
                goto clean;
-       }
 
-       l2cap_pi(sk)->chan = chan;
+       sk = chan->sk;
 
        write_lock_bh(&conn->chan_lock);
 
        hci_conn_hold(conn->hcon);
 
-       l2cap_sock_init(sk, parent);
-
        bacpy(&bt_sk(sk)->src, conn->src);
        bacpy(&bt_sk(sk)->dst, conn->dst);
 
@@ -867,9 +874,9 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn)
 
        __l2cap_chan_add(conn, chan);
 
-       l2cap_chan_set_timer(chan, sk->sk_sndtimeo);
+       __set_chan_timer(chan, sk->sk_sndtimeo);
 
-       sk->sk_state = BT_CONNECTED;
+       l2cap_state_change(chan, BT_CONNECTED);
        parent->sk_data_ready(parent, 0);
 
        write_unlock_bh(&conn->chan_lock);
@@ -878,6 +885,23 @@ clean:
        bh_unlock_sock(parent);
 }
 
+static void l2cap_chan_ready(struct sock *sk)
+{
+       struct l2cap_chan *chan = l2cap_pi(sk)->chan;
+       struct sock *parent = bt_sk(sk)->parent;
+
+       BT_DBG("sk %p, parent %p", sk, parent);
+
+       chan->conf_state = 0;
+       __clear_chan_timer(chan);
+
+       l2cap_state_change(chan, BT_CONNECTED);
+       sk->sk_state_change(sk);
+
+       if (parent)
+               parent->sk_data_ready(parent, 0);
+}
+
 static void l2cap_conn_ready(struct l2cap_conn *conn)
 {
        struct l2cap_chan *chan;
@@ -895,16 +919,15 @@ static void l2cap_conn_ready(struct l2cap_conn *conn)
                bh_lock_sock(sk);
 
                if (conn->hcon->type == LE_LINK) {
-                       l2cap_chan_clear_timer(chan);
-                       sk->sk_state = BT_CONNECTED;
-                       sk->sk_state_change(sk);
-               }
+                       if (smp_conn_security(conn, chan->sec_level))
+                               l2cap_chan_ready(sk);
 
-               if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
-                       l2cap_chan_clear_timer(chan);
-                       sk->sk_state = BT_CONNECTED;
+               } else if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
+                       __clear_chan_timer(chan);
+                       l2cap_state_change(chan, BT_CONNECTED);
                        sk->sk_state_change(sk);
-               } else if (sk->sk_state == BT_CONNECT)
+
+               } else if (chan->state == BT_CONNECT)
                        l2cap_do_start(chan);
 
                bh_unlock_sock(sk);
@@ -942,6 +965,45 @@ static void l2cap_info_timeout(unsigned long arg)
        l2cap_conn_start(conn);
 }
 
+static void l2cap_conn_del(struct hci_conn *hcon, int err)
+{
+       struct l2cap_conn *conn = hcon->l2cap_data;
+       struct l2cap_chan *chan, *l;
+       struct sock *sk;
+
+       if (!conn)
+               return;
+
+       BT_DBG("hcon %p conn %p, err %d", hcon, conn, err);
+
+       kfree_skb(conn->rx_skb);
+
+       /* Kill channels */
+       list_for_each_entry_safe(chan, l, &conn->chan_l, list) {
+               sk = chan->sk;
+               bh_lock_sock(sk);
+               l2cap_chan_del(chan, err);
+               bh_unlock_sock(sk);
+               chan->ops->close(chan->data);
+       }
+
+       if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT)
+               del_timer_sync(&conn->info_timer);
+
+       if (test_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend))
+               del_timer(&conn->security_timer);
+
+       hcon->l2cap_data = NULL;
+       kfree(conn);
+}
+
+static void security_timeout(unsigned long arg)
+{
+       struct l2cap_conn *conn = (void *) arg;
+
+       l2cap_conn_del(conn->hcon, ETIMEDOUT);
+}
+
 static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status)
 {
        struct l2cap_conn *conn = hcon->l2cap_data;
@@ -973,7 +1035,10 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status)
 
        INIT_LIST_HEAD(&conn->chan_l);
 
-       if (hcon->type != LE_LINK)
+       if (hcon->type == LE_LINK)
+               setup_timer(&conn->security_timer, security_timeout,
+                                               (unsigned long) conn);
+       else
                setup_timer(&conn->info_timer, l2cap_info_timeout,
                                                (unsigned long) conn);
 
@@ -982,35 +1047,6 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status)
        return conn;
 }
 
-static void l2cap_conn_del(struct hci_conn *hcon, int err)
-{
-       struct l2cap_conn *conn = hcon->l2cap_data;
-       struct l2cap_chan *chan, *l;
-       struct sock *sk;
-
-       if (!conn)
-               return;
-
-       BT_DBG("hcon %p conn %p, err %d", hcon, conn, err);
-
-       kfree_skb(conn->rx_skb);
-
-       /* Kill channels */
-       list_for_each_entry_safe(chan, l, &conn->chan_l, list) {
-               sk = chan->sk;
-               bh_lock_sock(sk);
-               l2cap_chan_del(chan, err);
-               bh_unlock_sock(sk);
-               l2cap_sock_kill(sk);
-       }
-
-       if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT)
-               del_timer_sync(&conn->info_timer);
-
-       hcon->l2cap_data = NULL;
-       kfree(conn);
-}
-
 static inline void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
 {
        write_lock_bh(&conn->chan_lock);
@@ -1032,7 +1068,7 @@ static struct l2cap_chan *l2cap_global_chan_by_psm(int state, __le16 psm, bdaddr
        list_for_each_entry(c, &chan_list, global_l) {
                struct sock *sk = c->sk;
 
-               if (state && sk->sk_state != state)
+               if (state && c->state != state)
                        continue;
 
                if (c->psm == psm) {
@@ -1099,14 +1135,14 @@ int l2cap_chan_connect(struct l2cap_chan *chan)
 
        l2cap_chan_add(conn, chan);
 
-       sk->sk_state = BT_CONNECT;
-       l2cap_chan_set_timer(chan, sk->sk_sndtimeo);
+       l2cap_state_change(chan, BT_CONNECT);
+       __set_chan_timer(chan, sk->sk_sndtimeo);
 
        if (hcon->state == BT_CONNECTED) {
                if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
-                       l2cap_chan_clear_timer(chan);
+                       __clear_chan_timer(chan);
                        if (l2cap_check_security(chan))
-                               sk->sk_state = BT_CONNECTED;
+                               l2cap_state_change(chan, BT_CONNECTED);
                } else
                        l2cap_do_start(chan);
        }
@@ -1166,7 +1202,7 @@ static void l2cap_monitor_timeout(unsigned long arg)
        }
 
        chan->retry_count++;
-       __mod_monitor_timer();
+       __set_monitor_timer(chan);
 
        l2cap_send_rr_or_rnr(chan, L2CAP_CTRL_POLL);
        bh_unlock_sock(sk);
@@ -1181,9 +1217,9 @@ static void l2cap_retrans_timeout(unsigned long arg)
 
        bh_lock_sock(sk);
        chan->retry_count = 1;
-       __mod_monitor_timer();
+       __set_monitor_timer(chan);
 
-       chan->conn_state |= L2CAP_CONN_WAIT_F;
+       set_bit(CONN_WAIT_F, &chan->conn_state);
 
        l2cap_send_rr_or_rnr(chan, L2CAP_CTRL_POLL);
        bh_unlock_sock(sk);
@@ -1205,7 +1241,7 @@ static void l2cap_drop_acked_frames(struct l2cap_chan *chan)
        }
 
        if (!chan->unacked_frames)
-               del_timer(&chan->retrans_timer);
+               __clear_retrans_timer(chan);
 }
 
 void l2cap_do_send(struct l2cap_chan *chan, struct sk_buff *skb)
@@ -1274,10 +1310,8 @@ static void l2cap_retransmit_one_frame(struct l2cap_chan *chan, u8 tx_seq)
        control = get_unaligned_le16(tx_skb->data + L2CAP_HDR_SIZE);
        control &= L2CAP_CTRL_SAR;
 
-       if (chan->conn_state & L2CAP_CONN_SEND_FBIT) {
+       if (test_and_clear_bit(CONN_SEND_FBIT, &chan->conn_state))
                control |= L2CAP_CTRL_FINAL;
-               chan->conn_state &= ~L2CAP_CONN_SEND_FBIT;
-       }
 
        control |= (chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT)
                        | (tx_seq << L2CAP_CTRL_TXSEQ_SHIFT);
@@ -1295,11 +1329,10 @@ static void l2cap_retransmit_one_frame(struct l2cap_chan *chan, u8 tx_seq)
 int l2cap_ertm_send(struct l2cap_chan *chan)
 {
        struct sk_buff *skb, *tx_skb;
-       struct sock *sk = chan->sk;
        u16 control, fcs;
        int nsent = 0;
 
-       if (sk->sk_state != BT_CONNECTED)
+       if (chan->state != BT_CONNECTED)
                return -ENOTCONN;
 
        while ((skb = chan->tx_send_head) && (!l2cap_tx_window_full(chan))) {
@@ -1317,10 +1350,9 @@ int l2cap_ertm_send(struct l2cap_chan *chan)
                control = get_unaligned_le16(tx_skb->data + L2CAP_HDR_SIZE);
                control &= L2CAP_CTRL_SAR;
 
-               if (chan->conn_state & L2CAP_CONN_SEND_FBIT) {
+               if (test_and_clear_bit(CONN_SEND_FBIT, &chan->conn_state))
                        control |= L2CAP_CTRL_FINAL;
-                       chan->conn_state &= ~L2CAP_CONN_SEND_FBIT;
-               }
+
                control |= (chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT)
                                | (chan->next_tx_seq << L2CAP_CTRL_TXSEQ_SHIFT);
                put_unaligned_le16(control, tx_skb->data + L2CAP_HDR_SIZE);
@@ -1333,7 +1365,7 @@ int l2cap_ertm_send(struct l2cap_chan *chan)
 
                l2cap_do_send(chan, tx_skb);
 
-               __mod_retrans_timer();
+               __set_retrans_timer(chan);
 
                bt_cb(skb)->tx_seq = chan->next_tx_seq;
                chan->next_tx_seq = (chan->next_tx_seq + 1) % 64;
@@ -1372,9 +1404,9 @@ static void l2cap_send_ack(struct l2cap_chan *chan)
 
        control |= chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT;
 
-       if (chan->conn_state & L2CAP_CONN_LOCAL_BUSY) {
+       if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) {
                control |= L2CAP_SUPER_RCV_NOT_READY;
-               chan->conn_state |= L2CAP_CONN_RNR_SENT;
+               set_bit(CONN_RNR_SENT, &chan->conn_state);
                l2cap_send_sframe(chan, control);
                return;
        }
@@ -1641,8 +1673,8 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len)
                        break;
                }
 
-               if ((chan->conn_state & L2CAP_CONN_REMOTE_BUSY) &&
-                               (chan->conn_state & L2CAP_CONN_WAIT_F)) {
+               if (test_bit(CONN_REMOTE_BUSY, &chan->conn_state) &&
+                               test_bit(CONN_WAIT_F, &chan->conn_state)) {
                        err = len;
                        break;
                }
@@ -1661,30 +1693,6 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len)
        return err;
 }
 
-static void l2cap_chan_ready(struct sock *sk)
-{
-       struct sock *parent = bt_sk(sk)->parent;
-       struct l2cap_chan *chan = l2cap_pi(sk)->chan;
-
-       BT_DBG("sk %p, parent %p", sk, parent);
-
-       chan->conf_state = 0;
-       l2cap_chan_clear_timer(chan);
-
-       if (!parent) {
-               /* Outgoing channel.
-                * Wake up socket sleeping on connect.
-                */
-               sk->sk_state = BT_CONNECTED;
-               sk->sk_state_change(sk);
-       } else {
-               /* Incoming channel.
-                * Wake up socket sleeping on accept.
-                */
-               parent->sk_data_ready(parent, 0);
-       }
-}
-
 /* Copy frame to all raw sockets on that connection */
 static void l2cap_raw_recv(struct l2cap_conn *conn, struct sk_buff *skb)
 {
@@ -1706,7 +1714,7 @@ static void l2cap_raw_recv(struct l2cap_conn *conn, struct sk_buff *skb)
                if (!nskb)
                        continue;
 
-               if (sock_queue_rcv_skb(sk, nskb))
+               if (chan->ops->recv(chan->data, nskb))
                        kfree_skb(nskb);
        }
        read_unlock(&conn->chan_lock);
@@ -1901,7 +1909,7 @@ static int l2cap_build_conf_req(struct l2cap_chan *chan, void *data)
        switch (chan->mode) {
        case L2CAP_MODE_STREAMING:
        case L2CAP_MODE_ERTM:
-               if (chan->conf_state & L2CAP_CONF_STATE2_DEVICE)
+               if (test_bit(CONF_STATE2_DEVICE, &chan->conf_state))
                        break;
 
                /* fall through */
@@ -1948,7 +1956,7 @@ done:
                        break;
 
                if (chan->fcs == L2CAP_FCS_NONE ||
-                               chan->conf_state & L2CAP_CONF_NO_FCS_RECV) {
+                               test_bit(CONF_NO_FCS_RECV, &chan->conf_state)) {
                        chan->fcs = L2CAP_FCS_NONE;
                        l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1, chan->fcs);
                }
@@ -1971,7 +1979,7 @@ done:
                        break;
 
                if (chan->fcs == L2CAP_FCS_NONE ||
-                               chan->conf_state & L2CAP_CONF_NO_FCS_RECV) {
+                               test_bit(CONF_NO_FCS_RECV, &chan->conf_state)) {
                        chan->fcs = L2CAP_FCS_NONE;
                        l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1, chan->fcs);
                }
@@ -2023,7 +2031,7 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data)
 
                case L2CAP_CONF_FCS:
                        if (val == L2CAP_FCS_NONE)
-                               chan->conf_state |= L2CAP_CONF_NO_FCS_RECV;
+                               set_bit(CONF_NO_FCS_RECV, &chan->conf_state);
 
                        break;
 
@@ -2043,7 +2051,7 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data)
        switch (chan->mode) {
        case L2CAP_MODE_STREAMING:
        case L2CAP_MODE_ERTM:
-               if (!(chan->conf_state & L2CAP_CONF_STATE2_DEVICE)) {
+               if (!test_bit(CONF_STATE2_DEVICE, &chan->conf_state)) {
                        chan->mode = l2cap_select_mode(rfc.mode,
                                        chan->conn->feat_mask);
                        break;
@@ -2076,14 +2084,14 @@ done:
                        result = L2CAP_CONF_UNACCEPT;
                else {
                        chan->omtu = mtu;
-                       chan->conf_state |= L2CAP_CONF_MTU_DONE;
+                       set_bit(CONF_MTU_DONE, &chan->conf_state);
                }
                l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, chan->omtu);
 
                switch (rfc.mode) {
                case L2CAP_MODE_BASIC:
                        chan->fcs = L2CAP_FCS_NONE;
-                       chan->conf_state |= L2CAP_CONF_MODE_DONE;
+                       set_bit(CONF_MODE_DONE, &chan->conf_state);
                        break;
 
                case L2CAP_MODE_ERTM:
@@ -2100,7 +2108,7 @@ done:
                        rfc.monitor_timeout =
                                le16_to_cpu(L2CAP_DEFAULT_MONITOR_TO);
 
-                       chan->conf_state |= L2CAP_CONF_MODE_DONE;
+                       set_bit(CONF_MODE_DONE, &chan->conf_state);
 
                        l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC,
                                        sizeof(rfc), (unsigned long) &rfc);
@@ -2113,7 +2121,7 @@ done:
 
                        chan->remote_mps = le16_to_cpu(rfc.max_pdu_size);
 
-                       chan->conf_state |= L2CAP_CONF_MODE_DONE;
+                       set_bit(CONF_MODE_DONE, &chan->conf_state);
 
                        l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC,
                                        sizeof(rfc), (unsigned long) &rfc);
@@ -2128,7 +2136,7 @@ done:
                }
 
                if (result == L2CAP_CONF_SUCCESS)
-                       chan->conf_state |= L2CAP_CONF_OUTPUT_DONE;
+                       set_bit(CONF_OUTPUT_DONE, &chan->conf_state);
        }
        rsp->scid   = cpu_to_le16(chan->dcid);
        rsp->result = cpu_to_le16(result);
@@ -2170,7 +2178,7 @@ static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len, voi
                        if (olen == sizeof(rfc))
                                memcpy(&rfc, (void *)val, olen);
 
-                       if ((chan->conf_state & L2CAP_CONF_STATE2_DEVICE) &&
+                       if (test_bit(CONF_STATE2_DEVICE, &chan->conf_state) &&
                                                        rfc.mode != chan->mode)
                                return -ECONNREFUSED;
 
@@ -2232,10 +2240,9 @@ void __l2cap_connect_rsp_defer(struct l2cap_chan *chan)
        l2cap_send_cmd(conn, chan->ident,
                                L2CAP_CONN_RSP, sizeof(rsp), &rsp);
 
-       if (chan->conf_state & L2CAP_CONF_REQ_SENT)
+       if (test_and_set_bit(CONF_REQ_SENT, &chan->conf_state))
                return;
 
-       chan->conf_state |= L2CAP_CONF_REQ_SENT;
        l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ,
                        l2cap_build_conf_req(chan, buf), buf);
        chan->num_conf_req++;
@@ -2335,17 +2342,11 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd
                goto response;
        }
 
-       sk = l2cap_sock_alloc(sock_net(parent), NULL, BTPROTO_L2CAP, GFP_ATOMIC);
-       if (!sk)
-               goto response;
-
-       chan = l2cap_chan_create(sk);
-       if (!chan) {
-               l2cap_sock_kill(sk);
+       chan = pchan->ops->new_connection(pchan->data);
+       if (!chan)
                goto response;
-       }
 
-       l2cap_pi(sk)->chan = chan;
+       sk = chan->sk;
 
        write_lock_bh(&conn->chan_lock);
 
@@ -2353,13 +2354,12 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd
        if (__l2cap_get_chan_by_dcid(conn, scid)) {
                write_unlock_bh(&conn->chan_lock);
                sock_set_flag(sk, SOCK_ZAPPED);
-               l2cap_sock_kill(sk);
+               chan->ops->close(chan->data);
                goto response;
        }
 
        hci_conn_hold(conn->hcon);
 
-       l2cap_sock_init(sk, parent);
        bacpy(&bt_sk(sk)->src, conn->src);
        bacpy(&bt_sk(sk)->dst, conn->dst);
        chan->psm  = psm;
@@ -2371,29 +2371,29 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd
 
        dcid = chan->scid;
 
-       l2cap_chan_set_timer(chan, sk->sk_sndtimeo);
+       __set_chan_timer(chan, sk->sk_sndtimeo);
 
        chan->ident = cmd->ident;
 
        if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE) {
                if (l2cap_check_security(chan)) {
                        if (bt_sk(sk)->defer_setup) {
-                               sk->sk_state = BT_CONNECT2;
+                               l2cap_state_change(chan, BT_CONNECT2);
                                result = L2CAP_CR_PEND;
                                status = L2CAP_CS_AUTHOR_PEND;
                                parent->sk_data_ready(parent, 0);
                        } else {
-                               sk->sk_state = BT_CONFIG;
+                               l2cap_state_change(chan, BT_CONFIG);
                                result = L2CAP_CR_SUCCESS;
                                status = L2CAP_CS_NO_INFO;
                        }
                } else {
-                       sk->sk_state = BT_CONNECT2;
+                       l2cap_state_change(chan, BT_CONNECT2);
                        result = L2CAP_CR_PEND;
                        status = L2CAP_CS_AUTHEN_PEND;
                }
        } else {
-               sk->sk_state = BT_CONNECT2;
+               l2cap_state_change(chan, BT_CONNECT2);
                result = L2CAP_CR_PEND;
                status = L2CAP_CS_NO_INFO;
        }
@@ -2424,10 +2424,10 @@ sendresp:
                                        L2CAP_INFO_REQ, sizeof(info), &info);
        }
 
-       if (chan && !(chan->conf_state & L2CAP_CONF_REQ_SENT) &&
+       if (chan && !test_bit(CONF_REQ_SENT, &chan->conf_state) &&
                                result == L2CAP_CR_SUCCESS) {
                u8 buf[128];
-               chan->conf_state |= L2CAP_CONF_REQ_SENT;
+               set_bit(CONF_REQ_SENT, &chan->conf_state);
                l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ,
                                        l2cap_build_conf_req(chan, buf), buf);
                chan->num_conf_req++;
@@ -2465,31 +2465,29 @@ static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hd
 
        switch (result) {
        case L2CAP_CR_SUCCESS:
-               sk->sk_state = BT_CONFIG;
+               l2cap_state_change(chan, BT_CONFIG);
                chan->ident = 0;
                chan->dcid = dcid;
-               chan->conf_state &= ~L2CAP_CONF_CONNECT_PEND;
+               clear_bit(CONF_CONNECT_PEND, &chan->conf_state);
 
-               if (chan->conf_state & L2CAP_CONF_REQ_SENT)
+               if (test_and_set_bit(CONF_REQ_SENT, &chan->conf_state))
                        break;
 
-               chan->conf_state |= L2CAP_CONF_REQ_SENT;
-
                l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ,
                                        l2cap_build_conf_req(chan, req), req);
                chan->num_conf_req++;
                break;
 
        case L2CAP_CR_PEND:
-               chan->conf_state |= L2CAP_CONF_CONNECT_PEND;
+               set_bit(CONF_CONNECT_PEND, &chan->conf_state);
                break;
 
        default:
                /* don't delete l2cap channel if sk is owned by user */
                if (sock_owned_by_user(sk)) {
-                       sk->sk_state = BT_DISCONN;
-                       l2cap_chan_clear_timer(chan);
-                       l2cap_chan_set_timer(chan, HZ / 5);
+                       l2cap_state_change(chan, BT_DISCONN);
+                       __clear_chan_timer(chan);
+                       __set_chan_timer(chan, HZ / 5);
                        break;
                }
 
@@ -2503,14 +2501,12 @@ static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hd
 
 static inline void set_default_fcs(struct l2cap_chan *chan)
 {
-       struct l2cap_pinfo *pi = l2cap_pi(chan->sk);
-
        /* FCS is enabled only in ERTM or streaming mode, if one or both
         * sides request it.
         */
        if (chan->mode != L2CAP_MODE_ERTM && chan->mode != L2CAP_MODE_STREAMING)
                chan->fcs = L2CAP_FCS_NONE;
-       else if (!(pi->chan->conf_state & L2CAP_CONF_NO_FCS_RECV))
+       else if (!test_bit(CONF_NO_FCS_RECV, &chan->conf_state))
                chan->fcs = L2CAP_FCS_CRC16;
 }
 
@@ -2534,7 +2530,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr
 
        sk = chan->sk;
 
-       if (sk->sk_state != BT_CONFIG) {
+       if (chan->state != BT_CONFIG) {
                struct l2cap_cmd_rej rej;
 
                rej.reason = cpu_to_le16(0x0002);
@@ -2577,13 +2573,13 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr
        /* Reset config buffer. */
        chan->conf_len = 0;
 
-       if (!(chan->conf_state & L2CAP_CONF_OUTPUT_DONE))
+       if (!test_bit(CONF_OUTPUT_DONE, &chan->conf_state))
                goto unlock;
 
-       if (chan->conf_state & L2CAP_CONF_INPUT_DONE) {
+       if (test_bit(CONF_INPUT_DONE, &chan->conf_state)) {
                set_default_fcs(chan);
 
-               sk->sk_state = BT_CONNECTED;
+               l2cap_state_change(chan, BT_CONNECTED);
 
                chan->next_tx_seq = 0;
                chan->expected_tx_seq = 0;
@@ -2595,9 +2591,8 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr
                goto unlock;
        }
 
-       if (!(chan->conf_state & L2CAP_CONF_REQ_SENT)) {
+       if (!test_and_set_bit(CONF_REQ_SENT, &chan->conf_state)) {
                u8 buf[64];
-               chan->conf_state |= L2CAP_CONF_REQ_SENT;
                l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ,
                                        l2cap_build_conf_req(chan, buf), buf);
                chan->num_conf_req++;
@@ -2662,7 +2657,7 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr
 
        default:
                sk->sk_err = ECONNRESET;
-               l2cap_chan_set_timer(chan, HZ * 5);
+               __set_chan_timer(chan, HZ * 5);
                l2cap_send_disconn_req(conn, chan, ECONNRESET);
                goto done;
        }
@@ -2670,12 +2665,12 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr
        if (flags & 0x01)
                goto done;
 
-       chan->conf_state |= L2CAP_CONF_INPUT_DONE;
+       set_bit(CONF_INPUT_DONE, &chan->conf_state);
 
-       if (chan->conf_state & L2CAP_CONF_OUTPUT_DONE) {
+       if (test_bit(CONF_OUTPUT_DONE, &chan->conf_state)) {
                set_default_fcs(chan);
 
-               sk->sk_state = BT_CONNECTED;
+               l2cap_state_change(chan, BT_CONNECTED);
                chan->next_tx_seq = 0;
                chan->expected_tx_seq = 0;
                skb_queue_head_init(&chan->tx_q);
@@ -2717,9 +2712,9 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn, struct l2cap_cmd
 
        /* don't delete l2cap channel if sk is owned by user */
        if (sock_owned_by_user(sk)) {
-               sk->sk_state = BT_DISCONN;
-               l2cap_chan_clear_timer(chan);
-               l2cap_chan_set_timer(chan, HZ / 5);
+               l2cap_state_change(chan, BT_DISCONN);
+               __clear_chan_timer(chan);
+               __set_chan_timer(chan, HZ / 5);
                bh_unlock_sock(sk);
                return 0;
        }
@@ -2727,7 +2722,7 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn, struct l2cap_cmd
        l2cap_chan_del(chan, ECONNRESET);
        bh_unlock_sock(sk);
 
-       l2cap_sock_kill(sk);
+       chan->ops->close(chan->data);
        return 0;
 }
 
@@ -2751,9 +2746,9 @@ 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)) {
-               sk->sk_state = BT_DISCONN;
-               l2cap_chan_clear_timer(chan);
-               l2cap_chan_set_timer(chan, HZ / 5);
+               l2cap_state_change(chan,BT_DISCONN);
+               __clear_chan_timer(chan);
+               __set_chan_timer(chan, HZ / 5);
                bh_unlock_sock(sk);
                return 0;
        }
@@ -2761,7 +2756,7 @@ static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, struct l2cap_cmd
        l2cap_chan_del(chan, 0);
        bh_unlock_sock(sk);
 
-       l2cap_sock_kill(sk);
+       chan->ops->close(chan->data);
        return 0;
 }
 
@@ -3069,18 +3064,18 @@ static inline void l2cap_send_i_or_rr_or_rnr(struct l2cap_chan *chan)
 
        control |= chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT;
 
-       if (chan->conn_state & L2CAP_CONN_LOCAL_BUSY) {
+       if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) {
                control |= L2CAP_SUPER_RCV_NOT_READY;
                l2cap_send_sframe(chan, control);
-               chan->conn_state |= L2CAP_CONN_RNR_SENT;
+               set_bit(CONN_RNR_SENT, &chan->conn_state);
        }
 
-       if (chan->conn_state & L2CAP_CONN_REMOTE_BUSY)
+       if (test_bit(CONN_REMOTE_BUSY, &chan->conn_state))
                l2cap_retransmit_frames(chan);
 
        l2cap_ertm_send(chan);
 
-       if (!(chan->conn_state & L2CAP_CONN_LOCAL_BUSY) &&
+       if (!test_bit(CONN_LOCAL_BUSY, &chan->conn_state) &&
                        chan->frames_sent == 0) {
                control |= L2CAP_SUPER_RCV_READY;
                l2cap_send_sframe(chan, control);
@@ -3136,13 +3131,13 @@ static int l2cap_ertm_reassembly_sdu(struct l2cap_chan *chan, struct sk_buff *sk
 
        switch (control & L2CAP_CTRL_SAR) {
        case L2CAP_SDU_UNSEGMENTED:
-               if (chan->conn_state & L2CAP_CONN_SAR_SDU)
+               if (test_bit(CONN_SAR_SDU, &chan->conn_state))
                        goto drop;
 
-               return sock_queue_rcv_skb(chan->sk, skb);
+               return chan->ops->recv(chan->data, skb);
 
        case L2CAP_SDU_START:
-               if (chan->conn_state & L2CAP_CONN_SAR_SDU)
+               if (test_bit(CONN_SAR_SDU, &chan->conn_state))
                        goto drop;
 
                chan->sdu_len = get_unaligned_le16(skb->data);
@@ -3161,12 +3156,12 @@ static int l2cap_ertm_reassembly_sdu(struct l2cap_chan *chan, struct sk_buff *sk
 
                memcpy(skb_put(chan->sdu, skb->len), skb->data, skb->len);
 
-               chan->conn_state |= L2CAP_CONN_SAR_SDU;
+               set_bit(CONN_SAR_SDU, &chan->conn_state);
                chan->partial_sdu_len = skb->len;
                break;
 
        case L2CAP_SDU_CONTINUE:
-               if (!(chan->conn_state & L2CAP_CONN_SAR_SDU))
+               if (!test_bit(CONN_SAR_SDU, &chan->conn_state))
                        goto disconnect;
 
                if (!chan->sdu)
@@ -3181,13 +3176,13 @@ static int l2cap_ertm_reassembly_sdu(struct l2cap_chan *chan, struct sk_buff *sk
                break;
 
        case L2CAP_SDU_END:
-               if (!(chan->conn_state & L2CAP_CONN_SAR_SDU))
+               if (!test_bit(CONN_SAR_SDU, &chan->conn_state))
                        goto disconnect;
 
                if (!chan->sdu)
                        goto disconnect;
 
-               if (!(chan->conn_state & L2CAP_CONN_SAR_RETRY)) {
+               if (!test_bit(CONN_SAR_RETRY, &chan->conn_state)) {
                        chan->partial_sdu_len += skb->len;
 
                        if (chan->partial_sdu_len > chan->imtu)
@@ -3201,19 +3196,19 @@ static int l2cap_ertm_reassembly_sdu(struct l2cap_chan *chan, struct sk_buff *sk
 
                _skb = skb_clone(chan->sdu, GFP_ATOMIC);
                if (!_skb) {
-                       chan->conn_state |= L2CAP_CONN_SAR_RETRY;
+                       set_bit(CONN_SAR_RETRY, &chan->conn_state);
                        return -ENOMEM;
                }
 
-               err = sock_queue_rcv_skb(chan->sk, _skb);
+               err = chan->ops->recv(chan->data, _skb);
                if (err < 0) {
                        kfree_skb(_skb);
-                       chan->conn_state |= L2CAP_CONN_SAR_RETRY;
+                       set_bit(CONN_SAR_RETRY, &chan->conn_state);
                        return err;
                }
 
-               chan->conn_state &= ~L2CAP_CONN_SAR_RETRY;
-               chan->conn_state &= ~L2CAP_CONN_SAR_SDU;
+               clear_bit(CONN_SAR_RETRY, &chan->conn_state);
+               clear_bit(CONN_SAR_SDU, &chan->conn_state);
 
                kfree_skb(chan->sdu);
                break;
@@ -3249,7 +3244,7 @@ static int l2cap_try_push_rx_skb(struct l2cap_chan *chan)
                chan->buffer_seq = (chan->buffer_seq + 1) % 64;
        }
 
-       if (!(chan->conn_state & L2CAP_CONN_RNR_SENT))
+       if (!test_bit(CONN_RNR_SENT, &chan->conn_state))
                goto done;
 
        control = chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT;
@@ -3257,14 +3252,14 @@ static int l2cap_try_push_rx_skb(struct l2cap_chan *chan)
        l2cap_send_sframe(chan, control);
        chan->retry_count = 1;
 
-       del_timer(&chan->retrans_timer);
-       __mod_monitor_timer();
+       __clear_retrans_timer(chan);
+       __set_monitor_timer(chan);
 
-       chan->conn_state |= L2CAP_CONN_WAIT_F;
+       set_bit(CONN_WAIT_F, &chan->conn_state);
 
 done:
-       chan->conn_state &= ~L2CAP_CONN_LOCAL_BUSY;
-       chan->conn_state &= ~L2CAP_CONN_RNR_SENT;
+       clear_bit(CONN_LOCAL_BUSY, &chan->conn_state);
+       clear_bit(CONN_RNR_SENT, &chan->conn_state);
 
        BT_DBG("chan %p, Exit local busy", chan);
 
@@ -3322,7 +3317,7 @@ static int l2cap_push_rx_skb(struct l2cap_chan *chan, struct sk_buff *skb, u16 c
 {
        int sctrl, err;
 
-       if (chan->conn_state & L2CAP_CONN_LOCAL_BUSY) {
+       if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) {
                bt_cb(skb)->sar = control >> L2CAP_CTRL_SAR_SHIFT;
                __skb_queue_tail(&chan->busy_q, skb);
                return l2cap_try_push_rx_skb(chan);
@@ -3339,7 +3334,7 @@ static int l2cap_push_rx_skb(struct l2cap_chan *chan, struct sk_buff *skb, u16 c
        /* Busy Condition */
        BT_DBG("chan %p, Enter local busy", chan);
 
-       chan->conn_state |= L2CAP_CONN_LOCAL_BUSY;
+       set_bit(CONN_LOCAL_BUSY, &chan->conn_state);
        bt_cb(skb)->sar = control >> L2CAP_CTRL_SAR_SHIFT;
        __skb_queue_tail(&chan->busy_q, skb);
 
@@ -3347,9 +3342,9 @@ static int l2cap_push_rx_skb(struct l2cap_chan *chan, struct sk_buff *skb, u16 c
        sctrl |= L2CAP_SUPER_RCV_NOT_READY;
        l2cap_send_sframe(chan, sctrl);
 
-       chan->conn_state |= L2CAP_CONN_RNR_SENT;
+       set_bit(CONN_RNR_SENT, &chan->conn_state);
 
-       del_timer(&chan->ack_timer);
+       __clear_ack_timer(chan);
 
        queue_work(_busy_wq, &chan->busy_work);
 
@@ -3368,19 +3363,19 @@ static int l2cap_streaming_reassembly_sdu(struct l2cap_chan *chan, struct sk_buf
 
        switch (control & L2CAP_CTRL_SAR) {
        case L2CAP_SDU_UNSEGMENTED:
-               if (chan->conn_state & L2CAP_CONN_SAR_SDU) {
+               if (test_bit(CONN_SAR_SDU, &chan->conn_state)) {
                        kfree_skb(chan->sdu);
                        break;
                }
 
-               err = sock_queue_rcv_skb(chan->sk, skb);
+               err = chan->ops->recv(chan->data, skb);
                if (!err)
                        return 0;
 
                break;
 
        case L2CAP_SDU_START:
-               if (chan->conn_state & L2CAP_CONN_SAR_SDU) {
+               if (test_bit(CONN_SAR_SDU, &chan->conn_state)) {
                        kfree_skb(chan->sdu);
                        break;
                }
@@ -3401,13 +3396,13 @@ static int l2cap_streaming_reassembly_sdu(struct l2cap_chan *chan, struct sk_buf
 
                memcpy(skb_put(chan->sdu, skb->len), skb->data, skb->len);
 
-               chan->conn_state |= L2CAP_CONN_SAR_SDU;
+               set_bit(CONN_SAR_SDU, &chan->conn_state);
                chan->partial_sdu_len = skb->len;
                err = 0;
                break;
 
        case L2CAP_SDU_CONTINUE:
-               if (!(chan->conn_state & L2CAP_CONN_SAR_SDU))
+               if (!test_bit(CONN_SAR_SDU, &chan->conn_state))
                        break;
 
                memcpy(skb_put(chan->sdu, skb->len), skb->data, skb->len);
@@ -3421,12 +3416,12 @@ static int l2cap_streaming_reassembly_sdu(struct l2cap_chan *chan, struct sk_buf
                break;
 
        case L2CAP_SDU_END:
-               if (!(chan->conn_state & L2CAP_CONN_SAR_SDU))
+               if (!test_bit(CONN_SAR_SDU, &chan->conn_state))
                        break;
 
                memcpy(skb_put(chan->sdu, skb->len), skb->data, skb->len);
 
-               chan->conn_state &= ~L2CAP_CONN_SAR_SDU;
+               clear_bit(CONN_SAR_SDU, &chan->conn_state);
                chan->partial_sdu_len += skb->len;
 
                if (chan->partial_sdu_len > chan->imtu)
@@ -3434,7 +3429,7 @@ static int l2cap_streaming_reassembly_sdu(struct l2cap_chan *chan, struct sk_buf
 
                if (chan->partial_sdu_len == chan->sdu_len) {
                        _skb = skb_clone(chan->sdu, GFP_ATOMIC);
-                       err = sock_queue_rcv_skb(chan->sk, _skb);
+                       err = chan->ops->recv(chan->data, _skb);
                        if (err < 0)
                                kfree_skb(_skb);
                }
@@ -3517,11 +3512,11 @@ static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u16 rx_cont
                                                        tx_seq, rx_control);
 
        if (L2CAP_CTRL_FINAL & rx_control &&
-                       chan->conn_state & L2CAP_CONN_WAIT_F) {
-               del_timer(&chan->monitor_timer);
+                       test_bit(CONN_WAIT_F, &chan->conn_state)) {
+               __clear_monitor_timer(chan);
                if (chan->unacked_frames > 0)
-                       __mod_retrans_timer();
-               chan->conn_state &= ~L2CAP_CONN_WAIT_F;
+                       __set_retrans_timer(chan);
+               clear_bit(CONN_WAIT_F, &chan->conn_state);
        }
 
        chan->expected_ack_seq = req_seq;
@@ -3540,10 +3535,10 @@ static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u16 rx_cont
                goto drop;
        }
 
-       if (chan->conn_state & L2CAP_CONN_LOCAL_BUSY)
+       if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state))
                goto drop;
 
-       if (chan->conn_state & L2CAP_CONN_SREJ_SENT) {
+       if (test_bit(CONN_SREJ_SENT, &chan->conn_state)) {
                struct srej_list *first;
 
                first = list_first_entry(&chan->srej_l,
@@ -3557,7 +3552,7 @@ static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u16 rx_cont
 
                        if (list_empty(&chan->srej_l)) {
                                chan->buffer_seq = chan->buffer_seq_srej;
-                               chan->conn_state &= ~L2CAP_CONN_SREJ_SENT;
+                               clear_bit(CONN_SREJ_SENT, &chan->conn_state);
                                l2cap_send_ack(chan);
                                BT_DBG("chan %p, Exit SREJ_SENT", chan);
                        }
@@ -3586,7 +3581,7 @@ static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u16 rx_cont
                if (tx_seq_offset < expected_tx_seq_offset)
                        goto drop;
 
-               chan->conn_state |= L2CAP_CONN_SREJ_SENT;
+               set_bit(CONN_SREJ_SENT, &chan->conn_state);
 
                BT_DBG("chan %p, Enter SREJ", chan);
 
@@ -3597,18 +3592,18 @@ static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u16 rx_cont
                __skb_queue_head_init(&chan->busy_q);
                l2cap_add_to_srej_queue(chan, skb, tx_seq, sar);
 
-               chan->conn_state |= L2CAP_CONN_SEND_PBIT;
+               set_bit(CONN_SEND_PBIT, &chan->conn_state);
 
                l2cap_send_srejframe(chan, tx_seq);
 
-               del_timer(&chan->ack_timer);
+               __clear_ack_timer(chan);
        }
        return 0;
 
 expected:
        chan->expected_tx_seq = (chan->expected_tx_seq + 1) % 64;
 
-       if (chan->conn_state & L2CAP_CONN_SREJ_SENT) {
+       if (test_bit(CONN_SREJ_SENT, &chan->conn_state)) {
                bt_cb(skb)->tx_seq = tx_seq;
                bt_cb(skb)->sar = sar;
                __skb_queue_tail(&chan->srej_q, skb);
@@ -3620,13 +3615,11 @@ expected:
                return 0;
 
        if (rx_control & L2CAP_CTRL_FINAL) {
-               if (chan->conn_state & L2CAP_CONN_REJ_ACT)
-                       chan->conn_state &= ~L2CAP_CONN_REJ_ACT;
-               else
+               if (!test_and_clear_bit(CONN_REJ_ACT, &chan->conn_state))
                        l2cap_retransmit_frames(chan);
        }
 
-       __mod_ack_timer();
+       __set_ack_timer(chan);
 
        chan->num_acked = (chan->num_acked + 1) % num_to_ack;
        if (chan->num_acked == num_to_ack - 1)
@@ -3648,33 +3641,31 @@ static inline void l2cap_data_channel_rrframe(struct l2cap_chan *chan, u16 rx_co
        l2cap_drop_acked_frames(chan);
 
        if (rx_control & L2CAP_CTRL_POLL) {
-               chan->conn_state |= L2CAP_CONN_SEND_FBIT;
-               if (chan->conn_state & L2CAP_CONN_SREJ_SENT) {
-                       if ((chan->conn_state & L2CAP_CONN_REMOTE_BUSY) &&
+               set_bit(CONN_SEND_FBIT, &chan->conn_state);
+               if (test_bit(CONN_SREJ_SENT, &chan->conn_state)) {
+                       if (test_bit(CONN_REMOTE_BUSY, &chan->conn_state) &&
                                        (chan->unacked_frames > 0))
-                               __mod_retrans_timer();
+                               __set_retrans_timer(chan);
 
-                       chan->conn_state &= ~L2CAP_CONN_REMOTE_BUSY;
+                       clear_bit(CONN_REMOTE_BUSY, &chan->conn_state);
                        l2cap_send_srejtail(chan);
                } else {
                        l2cap_send_i_or_rr_or_rnr(chan);
                }
 
        } else if (rx_control & L2CAP_CTRL_FINAL) {
-               chan->conn_state &= ~L2CAP_CONN_REMOTE_BUSY;
+               clear_bit(CONN_REMOTE_BUSY, &chan->conn_state);
 
-               if (chan->conn_state & L2CAP_CONN_REJ_ACT)
-                       chan->conn_state &= ~L2CAP_CONN_REJ_ACT;
-               else
+               if (!test_and_clear_bit(CONN_REJ_ACT, &chan->conn_state))
                        l2cap_retransmit_frames(chan);
 
        } else {
-               if ((chan->conn_state & L2CAP_CONN_REMOTE_BUSY) &&
+               if (test_bit(CONN_REMOTE_BUSY, &chan->conn_state) &&
                                (chan->unacked_frames > 0))
-                       __mod_retrans_timer();
+                       __set_retrans_timer(chan);
 
-               chan->conn_state &= ~L2CAP_CONN_REMOTE_BUSY;
-               if (chan->conn_state & L2CAP_CONN_SREJ_SENT)
+               clear_bit(CONN_REMOTE_BUSY, &chan->conn_state);
+               if (test_bit(CONN_SREJ_SENT, &chan->conn_state))
                        l2cap_send_ack(chan);
                else
                        l2cap_ertm_send(chan);
@@ -3687,21 +3678,19 @@ static inline void l2cap_data_channel_rejframe(struct l2cap_chan *chan, u16 rx_c
 
        BT_DBG("chan %p, req_seq %d ctrl 0x%4.4x", chan, tx_seq, rx_control);
 
-       chan->conn_state &= ~L2CAP_CONN_REMOTE_BUSY;
+       clear_bit(CONN_REMOTE_BUSY, &chan->conn_state);
 
        chan->expected_ack_seq = tx_seq;
        l2cap_drop_acked_frames(chan);
 
        if (rx_control & L2CAP_CTRL_FINAL) {
-               if (chan->conn_state & L2CAP_CONN_REJ_ACT)
-                       chan->conn_state &= ~L2CAP_CONN_REJ_ACT;
-               else
+               if (!test_and_clear_bit(CONN_REJ_ACT, &chan->conn_state))
                        l2cap_retransmit_frames(chan);
        } else {
                l2cap_retransmit_frames(chan);
 
-               if (chan->conn_state & L2CAP_CONN_WAIT_F)
-                       chan->conn_state |= L2CAP_CONN_REJ_ACT;
+               if (test_bit(CONN_WAIT_F, &chan->conn_state))
+                       set_bit(CONN_REJ_ACT, &chan->conn_state);
        }
 }
 static inline void l2cap_data_channel_srejframe(struct l2cap_chan *chan, u16 rx_control)
@@ -3710,32 +3699,32 @@ static inline void l2cap_data_channel_srejframe(struct l2cap_chan *chan, u16 rx_
 
        BT_DBG("chan %p, req_seq %d ctrl 0x%4.4x", chan, tx_seq, rx_control);
 
-       chan->conn_state &= ~L2CAP_CONN_REMOTE_BUSY;
+       clear_bit(CONN_REMOTE_BUSY, &chan->conn_state);
 
        if (rx_control & L2CAP_CTRL_POLL) {
                chan->expected_ack_seq = tx_seq;
                l2cap_drop_acked_frames(chan);
 
-               chan->conn_state |= L2CAP_CONN_SEND_FBIT;
+               set_bit(CONN_SEND_FBIT, &chan->conn_state);
                l2cap_retransmit_one_frame(chan, tx_seq);
 
                l2cap_ertm_send(chan);
 
-               if (chan->conn_state & L2CAP_CONN_WAIT_F) {
+               if (test_bit(CONN_WAIT_F, &chan->conn_state)) {
                        chan->srej_save_reqseq = tx_seq;
-                       chan->conn_state |= L2CAP_CONN_SREJ_ACT;
+                       set_bit(CONN_SREJ_ACT, &chan->conn_state);
                }
        } else if (rx_control & L2CAP_CTRL_FINAL) {
-               if ((chan->conn_state & L2CAP_CONN_SREJ_ACT) &&
+               if (test_bit(CONN_SREJ_ACT, &chan->conn_state) &&
                                chan->srej_save_reqseq == tx_seq)
-                       chan->conn_state &= ~L2CAP_CONN_SREJ_ACT;
+                       clear_bit(CONN_SREJ_ACT, &chan->conn_state);
                else
                        l2cap_retransmit_one_frame(chan, tx_seq);
        } else {
                l2cap_retransmit_one_frame(chan, tx_seq);
-               if (chan->conn_state & L2CAP_CONN_WAIT_F) {
+               if (test_bit(CONN_WAIT_F, &chan->conn_state)) {
                        chan->srej_save_reqseq = tx_seq;
-                       chan->conn_state |= L2CAP_CONN_SREJ_ACT;
+                       set_bit(CONN_SREJ_ACT, &chan->conn_state);
                }
        }
 }
@@ -3746,15 +3735,15 @@ static inline void l2cap_data_channel_rnrframe(struct l2cap_chan *chan, u16 rx_c
 
        BT_DBG("chan %p, req_seq %d ctrl 0x%4.4x", chan, tx_seq, rx_control);
 
-       chan->conn_state |= L2CAP_CONN_REMOTE_BUSY;
+       set_bit(CONN_REMOTE_BUSY, &chan->conn_state);
        chan->expected_ack_seq = tx_seq;
        l2cap_drop_acked_frames(chan);
 
        if (rx_control & L2CAP_CTRL_POLL)
-               chan->conn_state |= L2CAP_CONN_SEND_FBIT;
+               set_bit(CONN_SEND_FBIT, &chan->conn_state);
 
-       if (!(chan->conn_state & L2CAP_CONN_SREJ_SENT)) {
-               del_timer(&chan->retrans_timer);
+       if (!test_bit(CONN_SREJ_SENT, &chan->conn_state)) {
+               __clear_retrans_timer(chan);
                if (rx_control & L2CAP_CTRL_POLL)
                        l2cap_send_rr_or_rnr(chan, L2CAP_CTRL_FINAL);
                return;
@@ -3771,11 +3760,11 @@ static inline int l2cap_data_channel_sframe(struct l2cap_chan *chan, u16 rx_cont
        BT_DBG("chan %p rx_control 0x%4.4x len %d", chan, rx_control, skb->len);
 
        if (L2CAP_CTRL_FINAL & rx_control &&
-                       chan->conn_state & L2CAP_CONN_WAIT_F) {
-               del_timer(&chan->monitor_timer);
+                       test_bit(CONN_WAIT_F, &chan->conn_state)) {
+               __clear_monitor_timer(chan);
                if (chan->unacked_frames > 0)
-                       __mod_retrans_timer();
-               chan->conn_state &= ~L2CAP_CONN_WAIT_F;
+                       __set_retrans_timer(chan);
+               clear_bit(CONN_WAIT_F, &chan->conn_state);
        }
 
        switch (rx_control & L2CAP_CTRL_SUPERVISE) {
@@ -3888,7 +3877,7 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk
 
        BT_DBG("chan %p, len %d", chan, skb->len);
 
-       if (sk->sk_state != BT_CONNECTED)
+       if (chan->state != BT_CONNECTED)
                goto drop;
 
        switch (chan->mode) {
@@ -3901,7 +3890,7 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk
                if (chan->imtu < skb->len)
                        goto drop;
 
-               if (!sock_queue_rcv_skb(sk, skb))
+               if (!chan->ops->recv(chan->data, skb))
                        goto done;
                break;
 
@@ -3973,13 +3962,13 @@ static inline int l2cap_conless_channel(struct l2cap_conn *conn, __le16 psm, str
 
        BT_DBG("sk %p, len %d", sk, skb->len);
 
-       if (sk->sk_state != BT_BOUND && sk->sk_state != BT_CONNECTED)
+       if (chan->state != BT_BOUND && chan->state != BT_CONNECTED)
                goto drop;
 
-       if (l2cap_pi(sk)->chan->imtu < skb->len)
+       if (chan->imtu < skb->len)
                goto drop;
 
-       if (!sock_queue_rcv_skb(sk, skb))
+       if (!chan->ops->recv(chan->data, skb))
                goto done;
 
 drop:
@@ -4006,13 +3995,13 @@ static inline int l2cap_att_channel(struct l2cap_conn *conn, __le16 cid, struct
 
        BT_DBG("sk %p, len %d", sk, skb->len);
 
-       if (sk->sk_state != BT_BOUND && sk->sk_state != BT_CONNECTED)
+       if (chan->state != BT_BOUND && chan->state != BT_CONNECTED)
                goto drop;
 
-       if (l2cap_pi(sk)->chan->imtu < skb->len)
+       if (chan->imtu < skb->len)
                goto drop;
 
-       if (!sock_queue_rcv_skb(sk, skb))
+       if (!chan->ops->recv(chan->data, skb))
                goto done;
 
 drop:
@@ -4057,6 +4046,11 @@ static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb)
                l2cap_att_channel(conn, cid, skb);
                break;
 
+       case L2CAP_CID_SMP:
+               if (smp_sig_channel(conn, skb))
+                       l2cap_conn_del(conn->hcon, EACCES);
+               break;
+
        default:
                l2cap_data_channel(conn, cid, skb);
                break;
@@ -4080,7 +4074,7 @@ static int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
        list_for_each_entry(c, &chan_list, global_l) {
                struct sock *sk = c->sk;
 
-               if (sk->sk_state != BT_LISTEN)
+               if (c->state != BT_LISTEN)
                        continue;
 
                if (!bacmp(&bt_sk(sk)->src, &hdev->bdaddr)) {
@@ -4124,7 +4118,7 @@ static int l2cap_disconn_ind(struct hci_conn *hcon)
 
        BT_DBG("hcon %p", hcon);
 
-       if (hcon->type != ACL_LINK || !conn)
+       if ((hcon->type != ACL_LINK && hcon->type != LE_LINK) || !conn)
                return 0x13;
 
        return conn->disc_reason;
@@ -4149,13 +4143,13 @@ static inline void l2cap_check_encryption(struct l2cap_chan *chan, u8 encrypt)
 
        if (encrypt == 0x00) {
                if (chan->sec_level == BT_SECURITY_MEDIUM) {
-                       l2cap_chan_clear_timer(chan);
-                       l2cap_chan_set_timer(chan, HZ * 5);
+                       __clear_chan_timer(chan);
+                       __set_chan_timer(chan, HZ * 5);
                } else if (chan->sec_level == BT_SECURITY_HIGH)
                        l2cap_chan_close(chan, ECONNREFUSED);
        } else {
                if (chan->sec_level == BT_SECURITY_MEDIUM)
-                       l2cap_chan_clear_timer(chan);
+                       __clear_chan_timer(chan);
        }
 }
 
@@ -4176,50 +4170,72 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
 
                bh_lock_sock(sk);
 
-               if (chan->conf_state & L2CAP_CONF_CONNECT_PEND) {
+               BT_DBG("chan->scid %d", chan->scid);
+
+               if (chan->scid == L2CAP_CID_LE_DATA) {
+                       if (!status && encrypt) {
+                               chan->sec_level = hcon->sec_level;
+                               del_timer(&conn->security_timer);
+                               l2cap_chan_ready(sk);
+                       }
+
+                       bh_unlock_sock(sk);
+                       continue;
+               }
+
+               if (test_bit(CONF_CONNECT_PEND, &chan->conf_state)) {
                        bh_unlock_sock(sk);
                        continue;
                }
 
-               if (!status && (sk->sk_state == BT_CONNECTED ||
-                                               sk->sk_state == BT_CONFIG)) {
+               if (!status && (chan->state == BT_CONNECTED ||
+                                               chan->state == BT_CONFIG)) {
                        l2cap_check_encryption(chan, encrypt);
                        bh_unlock_sock(sk);
                        continue;
                }
 
-               if (sk->sk_state == BT_CONNECT) {
+               if (chan->state == BT_CONNECT) {
                        if (!status) {
                                struct l2cap_conn_req req;
                                req.scid = cpu_to_le16(chan->scid);
                                req.psm  = chan->psm;
 
                                chan->ident = l2cap_get_ident(conn);
-                               chan->conf_state |= L2CAP_CONF_CONNECT_PEND;
+                               set_bit(CONF_CONNECT_PEND, &chan->conf_state);
 
                                l2cap_send_cmd(conn, chan->ident,
                                        L2CAP_CONN_REQ, sizeof(req), &req);
                        } else {
-                               l2cap_chan_clear_timer(chan);
-                               l2cap_chan_set_timer(chan, HZ / 10);
+                               __clear_chan_timer(chan);
+                               __set_chan_timer(chan, HZ / 10);
                        }
-               } else if (sk->sk_state == BT_CONNECT2) {
+               } else if (chan->state == BT_CONNECT2) {
                        struct l2cap_conn_rsp rsp;
-                       __u16 result;
+                       __u16 res, stat;
 
                        if (!status) {
-                               sk->sk_state = BT_CONFIG;
-                               result = L2CAP_CR_SUCCESS;
+                               if (bt_sk(sk)->defer_setup) {
+                                       struct sock *parent = bt_sk(sk)->parent;
+                                       res = L2CAP_CR_PEND;
+                                       stat = L2CAP_CS_AUTHOR_PEND;
+                                       parent->sk_data_ready(parent, 0);
+                               } else {
+                                       l2cap_state_change(chan, BT_CONFIG);
+                                       res = L2CAP_CR_SUCCESS;
+                                       stat = L2CAP_CS_NO_INFO;
+                               }
                        } else {
-                               sk->sk_state = BT_DISCONN;
-                               l2cap_chan_set_timer(chan, HZ / 10);
-                               result = L2CAP_CR_SEC_BLOCK;
+                               l2cap_state_change(chan, BT_DISCONN);
+                               __set_chan_timer(chan, HZ / 10);
+                               res = L2CAP_CR_SEC_BLOCK;
+                               stat = L2CAP_CS_NO_INFO;
                        }
 
                        rsp.scid   = cpu_to_le16(chan->dcid);
                        rsp.dcid   = cpu_to_le16(chan->scid);
-                       rsp.result = cpu_to_le16(result);
-                       rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO);
+                       rsp.result = cpu_to_le16(res);
+                       rsp.status = cpu_to_le16(stat);
                        l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_RSP,
                                                        sizeof(rsp), &rsp);
                }
@@ -4355,10 +4371,10 @@ static int l2cap_debugfs_show(struct seq_file *f, void *p)
                seq_printf(f, "%s %s %d %d 0x%4.4x 0x%4.4x %d %d %d %d\n",
                                        batostr(&bt_sk(sk)->src),
                                        batostr(&bt_sk(sk)->dst),
-                                       sk->sk_state, __le16_to_cpu(c->psm),
+                                       c->state, __le16_to_cpu(c->psm),
                                        c->scid, c->dcid, c->imtu, c->omtu,
                                        c->sec_level, c->mode);
-       }
+}
 
        read_unlock_bh(&chan_list_lock);
 
index ab81894c667765861b55f425b720a9ad5ea32daf..39082d4e77cef96b6a99370b3bb1792eb4ce7f22 100644 (file)
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
 #include <net/bluetooth/l2cap.h>
+#include <net/bluetooth/smp.h>
 
 static const struct proto_ops l2cap_sock_ops;
+static void l2cap_sock_init(struct sock *sk, struct sock *parent);
+static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int proto, gfp_t prio);
 
 static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen)
 {
@@ -87,6 +90,8 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen)
                chan->sec_level = BT_SECURITY_SDP;
 
        bacpy(&bt_sk(sk)->src, &la.l2_bdaddr);
+
+       chan->state = BT_BOUND;
        sk->sk_state = BT_BOUND;
 
 done:
@@ -212,6 +217,8 @@ static int l2cap_sock_listen(struct socket *sock, int backlog)
 
        sk->sk_max_ack_backlog = backlog;
        sk->sk_ack_backlog = 0;
+
+       chan->state = BT_LISTEN;
        sk->sk_state = BT_LISTEN;
 
 done:
@@ -505,7 +512,7 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us
                chan->mode = opts.mode;
                switch (chan->mode) {
                case L2CAP_MODE_BASIC:
-                       chan->conf_state &= ~L2CAP_CONF_STATE2_DEVICE;
+                       clear_bit(CONF_STATE2_DEVICE, &chan->conf_state);
                        break;
                case L2CAP_MODE_ERTM:
                case L2CAP_MODE_STREAMING:
@@ -556,6 +563,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch
        struct l2cap_chan *chan = l2cap_pi(sk)->chan;
        struct bt_security sec;
        struct bt_power pwr;
+       struct l2cap_conn *conn;
        int len, err = 0;
        u32 opt;
 
@@ -592,6 +600,20 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch
                }
 
                chan->sec_level = sec.level;
+
+               conn = chan->conn;
+               if (conn && chan->scid == L2CAP_CID_LE_DATA) {
+                       if (!conn->hcon->out) {
+                               err = -EINVAL;
+                               break;
+                       }
+
+                       if (smp_conn_security(conn, sec.level))
+                               break;
+
+                       err = 0;
+                       sk->sk_state = BT_CONFIG;
+               }
                break;
 
        case BT_DEFER_SETUP:
@@ -711,7 +733,7 @@ static int l2cap_sock_recvmsg(struct kiocb *iocb, struct socket *sock, struct ms
 /* Kill socket (only if zapped and orphan)
  * Must be called on unlocked socket.
  */
-void l2cap_sock_kill(struct sock *sk)
+static void l2cap_sock_kill(struct sock *sk)
 {
        if (!sock_flag(sk, SOCK_ZAPPED) || sk->sk_socket)
                return;
@@ -773,6 +795,49 @@ static int l2cap_sock_release(struct socket *sock)
        return err;
 }
 
+static struct l2cap_chan *l2cap_sock_new_connection_cb(void *data)
+{
+       struct sock *sk, *parent = data;
+
+       sk = l2cap_sock_alloc(sock_net(parent), NULL, BTPROTO_L2CAP,
+                                                               GFP_ATOMIC);
+       if (!sk)
+               return NULL;
+
+       l2cap_sock_init(sk, parent);
+
+       return l2cap_pi(sk)->chan;
+}
+
+static int l2cap_sock_recv_cb(void *data, struct sk_buff *skb)
+{
+       struct sock *sk = data;
+
+       return sock_queue_rcv_skb(sk, skb);
+}
+
+static void l2cap_sock_close_cb(void *data)
+{
+       struct sock *sk = data;
+
+       l2cap_sock_kill(sk);
+}
+
+static void l2cap_sock_state_change_cb(void *data, int state)
+{
+       struct sock *sk = data;
+
+       sk->sk_state = state;
+}
+
+static struct l2cap_ops l2cap_chan_ops = {
+       .name           = "L2CAP Socket Interface",
+       .new_connection = l2cap_sock_new_connection_cb,
+       .recv           = l2cap_sock_recv_cb,
+       .close          = l2cap_sock_close_cb,
+       .state_change   = l2cap_sock_state_change_cb,
+};
+
 static void l2cap_sock_destruct(struct sock *sk)
 {
        BT_DBG("sk %p", sk);
@@ -781,7 +846,7 @@ static void l2cap_sock_destruct(struct sock *sk)
        skb_queue_purge(&sk->sk_write_queue);
 }
 
-void l2cap_sock_init(struct sock *sk, struct sock *parent)
+static void l2cap_sock_init(struct sock *sk, struct sock *parent)
 {
        struct l2cap_pinfo *pi = l2cap_pi(sk);
        struct l2cap_chan *chan = pi->chan;
@@ -826,7 +891,7 @@ void l2cap_sock_init(struct sock *sk, struct sock *parent)
                chan->omtu = 0;
                if (!disable_ertm && sk->sk_type == SOCK_STREAM) {
                        chan->mode = L2CAP_MODE_ERTM;
-                       chan->conf_state |= L2CAP_CONF_STATE2_DEVICE;
+                       set_bit(CONF_STATE2_DEVICE, &chan->conf_state);
                } else {
                        chan->mode = L2CAP_MODE_BASIC;
                }
@@ -838,10 +903,14 @@ void l2cap_sock_init(struct sock *sk, struct sock *parent)
                chan->force_reliable = 0;
                chan->flushable = BT_FLUSHABLE_OFF;
                chan->force_active = BT_POWER_FORCE_ACTIVE_ON;
+
        }
 
        /* Default config options */
        chan->flush_to = L2CAP_DEFAULT_FLUSH_TO;
+
+       chan->data = sk;
+       chan->ops = &l2cap_chan_ops;
 }
 
 static struct proto l2cap_proto = {
@@ -850,9 +919,10 @@ static struct proto l2cap_proto = {
        .obj_size       = sizeof(struct l2cap_pinfo)
 };
 
-struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int proto, gfp_t prio)
+static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int proto, gfp_t prio)
 {
        struct sock *sk;
+       struct l2cap_chan *chan;
 
        sk = sk_alloc(net, PF_BLUETOOTH, prio, &l2cap_proto);
        if (!sk)
@@ -869,6 +939,14 @@ struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int proto, g
        sk->sk_protocol = proto;
        sk->sk_state = BT_OPEN;
 
+       chan = l2cap_chan_create(sk);
+       if (!chan) {
+               l2cap_sock_kill(sk);
+               return NULL;
+       }
+
+       l2cap_pi(sk)->chan = chan;
+
        return sk;
 }
 
@@ -876,7 +954,6 @@ static int l2cap_sock_create(struct net *net, struct socket *sock, int protocol,
                             int kern)
 {
        struct sock *sk;
-       struct l2cap_chan *chan;
 
        BT_DBG("sock %p", sock);
 
@@ -895,14 +972,6 @@ static int l2cap_sock_create(struct net *net, struct socket *sock, int protocol,
        if (!sk)
                return -ENOMEM;
 
-       chan = l2cap_chan_create(sk);
-       if (!chan) {
-               l2cap_sock_kill(sk);
-               return -ENOMEM;
-       }
-
-       l2cap_pi(sk)->chan = chan;
-
        l2cap_sock_init(sk, NULL);
        return 0;
 }
index fcccf10f909a718a88292a530817d9c963163209..64c0418a622180e936b8cee1ad1f5d642dbf003b 100644 (file)
@@ -990,7 +990,7 @@ static int remove_key(struct sock *sk, u16 index, unsigned char *data, u16 len)
 
                put_unaligned_le16(conn->handle, &dc.handle);
                dc.reason = 0x13; /* Remote User Terminated Connection */
-               err = hci_send_cmd(hdev, HCI_OP_DISCONNECT, 0, NULL);
+               err = hci_send_cmd(hdev, HCI_OP_DISCONNECT, sizeof(dc), &dc);
        }
 
 unlock:
@@ -1666,6 +1666,70 @@ failed:
        return err;
 }
 
+static int block_device(struct sock *sk, u16 index, unsigned char *data,
+                                                               u16 len)
+{
+       struct hci_dev *hdev;
+       struct mgmt_cp_block_device *cp;
+       int err;
+
+       BT_DBG("hci%u", index);
+
+       cp = (void *) data;
+
+       if (len != sizeof(*cp))
+               return cmd_status(sk, index, MGMT_OP_BLOCK_DEVICE,
+                                                       EINVAL);
+
+       hdev = hci_dev_get(index);
+       if (!hdev)
+               return cmd_status(sk, index, MGMT_OP_BLOCK_DEVICE,
+                                                       ENODEV);
+
+       err = hci_blacklist_add(hdev, &cp->bdaddr);
+
+       if (err < 0)
+               err = cmd_status(sk, index, MGMT_OP_BLOCK_DEVICE, -err);
+       else
+               err = cmd_complete(sk, index, MGMT_OP_BLOCK_DEVICE,
+                                                       NULL, 0);
+       hci_dev_put(hdev);
+
+       return err;
+}
+
+static int unblock_device(struct sock *sk, u16 index, unsigned char *data,
+                                                               u16 len)
+{
+       struct hci_dev *hdev;
+       struct mgmt_cp_unblock_device *cp;
+       int err;
+
+       BT_DBG("hci%u", index);
+
+       cp = (void *) data;
+
+       if (len != sizeof(*cp))
+               return cmd_status(sk, index, MGMT_OP_UNBLOCK_DEVICE,
+                                                               EINVAL);
+
+       hdev = hci_dev_get(index);
+       if (!hdev)
+               return cmd_status(sk, index, MGMT_OP_UNBLOCK_DEVICE,
+                                                               ENODEV);
+
+       err = hci_blacklist_del(hdev, &cp->bdaddr);
+
+       if (err < 0)
+               err = cmd_status(sk, index, MGMT_OP_UNBLOCK_DEVICE, -err);
+       else
+               err = cmd_complete(sk, index, MGMT_OP_UNBLOCK_DEVICE,
+                                                               NULL, 0);
+       hci_dev_put(hdev);
+
+       return err;
+}
+
 int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
 {
        unsigned char *buf;
@@ -1780,6 +1844,12 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
        case MGMT_OP_STOP_DISCOVERY:
                err = stop_discovery(sk, index);
                break;
+       case MGMT_OP_BLOCK_DEVICE:
+               err = block_device(sk, index, buf + sizeof(*hdr), len);
+               break;
+       case MGMT_OP_UNBLOCK_DEVICE:
+               err = unblock_device(sk, index, buf + sizeof(*hdr), len);
+               break;
        default:
                BT_DBG("Unknown op %u", opcode);
                err = cmd_status(sk, index, opcode, 0x01);
diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c
new file mode 100644 (file)
index 0000000..52e9ec2
--- /dev/null
@@ -0,0 +1,533 @@
+/*
+   BlueZ - Bluetooth protocol stack for Linux
+   Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License version 2 as
+   published by the Free Software Foundation;
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
+   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
+   SOFTWARE IS DISCLAIMED.
+*/
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+#include <net/bluetooth/l2cap.h>
+#include <net/bluetooth/smp.h>
+#include <linux/crypto.h>
+#include <crypto/b128ops.h>
+
+#define SMP_TIMEOUT 30000 /* 30 seconds */
+
+static inline void swap128(u8 src[16], u8 dst[16])
+{
+       int i;
+       for (i = 0; i < 16; i++)
+               dst[15 - i] = src[i];
+}
+
+static inline void swap56(u8 src[7], u8 dst[7])
+{
+       int i;
+       for (i = 0; i < 7; i++)
+               dst[6 - i] = src[i];
+}
+
+static int smp_e(struct crypto_blkcipher *tfm, const u8 *k, u8 *r)
+{
+       struct blkcipher_desc desc;
+       struct scatterlist sg;
+       int err, iv_len;
+       unsigned char iv[128];
+
+       if (tfm == NULL) {
+               BT_ERR("tfm %p", tfm);
+               return -EINVAL;
+       }
+
+       desc.tfm = tfm;
+       desc.flags = 0;
+
+       err = crypto_blkcipher_setkey(tfm, k, 16);
+       if (err) {
+               BT_ERR("cipher setkey failed: %d", err);
+               return err;
+       }
+
+       sg_init_one(&sg, r, 16);
+
+       iv_len = crypto_blkcipher_ivsize(tfm);
+       if (iv_len) {
+               memset(&iv, 0xff, iv_len);
+               crypto_blkcipher_set_iv(tfm, iv, iv_len);
+       }
+
+       err = crypto_blkcipher_encrypt(&desc, &sg, &sg, 16);
+       if (err)
+               BT_ERR("Encrypt data error %d", err);
+
+       return err;
+}
+
+static int smp_c1(struct crypto_blkcipher *tfm, u8 k[16], u8 r[16],
+               u8 preq[7], u8 pres[7], u8 _iat, bdaddr_t *ia,
+               u8 _rat, bdaddr_t *ra, u8 res[16])
+{
+       u8 p1[16], p2[16];
+       int err;
+
+       memset(p1, 0, 16);
+
+       /* p1 = pres || preq || _rat || _iat */
+       swap56(pres, p1);
+       swap56(preq, p1 + 7);
+       p1[14] = _rat;
+       p1[15] = _iat;
+
+       memset(p2, 0, 16);
+
+       /* p2 = padding || ia || ra */
+       baswap((bdaddr_t *) (p2 + 4), ia);
+       baswap((bdaddr_t *) (p2 + 10), ra);
+
+       /* res = r XOR p1 */
+       u128_xor((u128 *) res, (u128 *) r, (u128 *) p1);
+
+       /* res = e(k, res) */
+       err = smp_e(tfm, k, res);
+       if (err) {
+               BT_ERR("Encrypt data error");
+               return err;
+       }
+
+       /* res = res XOR p2 */
+       u128_xor((u128 *) res, (u128 *) res, (u128 *) p2);
+
+       /* res = e(k, res) */
+       err = smp_e(tfm, k, res);
+       if (err)
+               BT_ERR("Encrypt data error");
+
+       return err;
+}
+
+static int smp_s1(struct crypto_blkcipher *tfm, u8 k[16],
+                       u8 r1[16], u8 r2[16], u8 _r[16])
+{
+       int err;
+
+       /* Just least significant octets from r1 and r2 are considered */
+       memcpy(_r, r1 + 8, 8);
+       memcpy(_r + 8, r2 + 8, 8);
+
+       err = smp_e(tfm, k, _r);
+       if (err)
+               BT_ERR("Encrypt data error");
+
+       return err;
+}
+
+static int smp_rand(u8 *buf)
+{
+       get_random_bytes(buf, 16);
+
+       return 0;
+}
+
+static struct sk_buff *smp_build_cmd(struct l2cap_conn *conn, u8 code,
+                                               u16 dlen, void *data)
+{
+       struct sk_buff *skb;
+       struct l2cap_hdr *lh;
+       int len;
+
+       len = L2CAP_HDR_SIZE + sizeof(code) + dlen;
+
+       if (len > conn->mtu)
+               return NULL;
+
+       skb = bt_skb_alloc(len, GFP_ATOMIC);
+       if (!skb)
+               return NULL;
+
+       lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE);
+       lh->len = cpu_to_le16(sizeof(code) + dlen);
+       lh->cid = cpu_to_le16(L2CAP_CID_SMP);
+
+       memcpy(skb_put(skb, sizeof(code)), &code, sizeof(code));
+
+       memcpy(skb_put(skb, dlen), data, dlen);
+
+       return skb;
+}
+
+static void smp_send_cmd(struct l2cap_conn *conn, u8 code, u16 len, void *data)
+{
+       struct sk_buff *skb = smp_build_cmd(conn, code, len, data);
+
+       BT_DBG("code 0x%2.2x", code);
+
+       if (!skb)
+               return;
+
+       hci_send_acl(conn->hcon, skb, 0);
+}
+
+static __u8 seclevel_to_authreq(__u8 level)
+{
+       switch (level) {
+       case BT_SECURITY_HIGH:
+               /* Right now we don't support bonding */
+               return SMP_AUTH_MITM;
+
+       default:
+               return SMP_AUTH_NONE;
+       }
+}
+
+static void build_pairing_cmd(struct l2cap_conn *conn,
+                               struct smp_cmd_pairing *cmd, __u8 authreq)
+{
+       cmd->io_capability = conn->hcon->io_capability;
+       cmd->oob_flag = SMP_OOB_NOT_PRESENT;
+       cmd->max_key_size = SMP_MAX_ENC_KEY_SIZE;
+       cmd->init_key_dist = 0x00;
+       cmd->resp_key_dist = 0x00;
+       cmd->auth_req = authreq;
+}
+
+static u8 check_enc_key_size(struct l2cap_conn *conn, __u8 max_key_size)
+{
+       if ((max_key_size > SMP_MAX_ENC_KEY_SIZE) ||
+                       (max_key_size < SMP_MIN_ENC_KEY_SIZE))
+               return SMP_ENC_KEY_SIZE;
+
+       conn->smp_key_size = max_key_size;
+
+       return 0;
+}
+
+static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
+{
+       struct smp_cmd_pairing rsp, *req = (void *) skb->data;
+       u8 key_size;
+
+       BT_DBG("conn %p", conn);
+
+       conn->preq[0] = SMP_CMD_PAIRING_REQ;
+       memcpy(&conn->preq[1], req, sizeof(*req));
+       skb_pull(skb, sizeof(*req));
+
+       if (req->oob_flag)
+               return SMP_OOB_NOT_AVAIL;
+
+       /* We didn't start the pairing, so no requirements */
+       build_pairing_cmd(conn, &rsp, SMP_AUTH_NONE);
+
+       key_size = min(req->max_key_size, rsp.max_key_size);
+       if (check_enc_key_size(conn, key_size))
+               return SMP_ENC_KEY_SIZE;
+
+       /* Just works */
+       memset(conn->tk, 0, sizeof(conn->tk));
+
+       conn->prsp[0] = SMP_CMD_PAIRING_RSP;
+       memcpy(&conn->prsp[1], &rsp, sizeof(rsp));
+
+       smp_send_cmd(conn, SMP_CMD_PAIRING_RSP, sizeof(rsp), &rsp);
+
+       mod_timer(&conn->security_timer, jiffies +
+                                       msecs_to_jiffies(SMP_TIMEOUT));
+
+       return 0;
+}
+
+static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb)
+{
+       struct smp_cmd_pairing *req, *rsp = (void *) skb->data;
+       struct smp_cmd_pairing_confirm cp;
+       struct crypto_blkcipher *tfm = conn->hcon->hdev->tfm;
+       int ret;
+       u8 res[16], key_size;
+
+       BT_DBG("conn %p", conn);
+
+       skb_pull(skb, sizeof(*rsp));
+
+       req = (void *) &conn->preq[1];
+
+       key_size = min(req->max_key_size, rsp->max_key_size);
+       if (check_enc_key_size(conn, key_size))
+               return SMP_ENC_KEY_SIZE;
+
+       if (rsp->oob_flag)
+               return SMP_OOB_NOT_AVAIL;
+
+       /* Just works */
+       memset(conn->tk, 0, sizeof(conn->tk));
+
+       conn->prsp[0] = SMP_CMD_PAIRING_RSP;
+       memcpy(&conn->prsp[1], rsp, sizeof(*rsp));
+
+       ret = smp_rand(conn->prnd);
+       if (ret)
+               return SMP_UNSPECIFIED;
+
+       ret = smp_c1(tfm, conn->tk, conn->prnd, conn->preq, conn->prsp, 0,
+                       conn->src, conn->hcon->dst_type, conn->dst, res);
+       if (ret)
+               return SMP_UNSPECIFIED;
+
+       swap128(res, cp.confirm_val);
+
+       smp_send_cmd(conn, SMP_CMD_PAIRING_CONFIRM, sizeof(cp), &cp);
+
+       return 0;
+}
+
+static u8 smp_cmd_pairing_confirm(struct l2cap_conn *conn, struct sk_buff *skb)
+{
+       struct crypto_blkcipher *tfm = conn->hcon->hdev->tfm;
+
+       BT_DBG("conn %p %s", conn, conn->hcon->out ? "master" : "slave");
+
+       memcpy(conn->pcnf, skb->data, sizeof(conn->pcnf));
+       skb_pull(skb, sizeof(conn->pcnf));
+
+       if (conn->hcon->out) {
+               u8 random[16];
+
+               swap128(conn->prnd, random);
+               smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(random),
+                                                               random);
+       } else {
+               struct smp_cmd_pairing_confirm cp;
+               int ret;
+               u8 res[16];
+
+               ret = smp_rand(conn->prnd);
+               if (ret)
+                       return SMP_UNSPECIFIED;
+
+               ret = smp_c1(tfm, conn->tk, conn->prnd, conn->preq, conn->prsp,
+                                               conn->hcon->dst_type, conn->dst,
+                                               0, conn->src, res);
+               if (ret)
+                       return SMP_CONFIRM_FAILED;
+
+               swap128(res, cp.confirm_val);
+
+               smp_send_cmd(conn, SMP_CMD_PAIRING_CONFIRM, sizeof(cp), &cp);
+       }
+
+       mod_timer(&conn->security_timer, jiffies +
+                                       msecs_to_jiffies(SMP_TIMEOUT));
+
+       return 0;
+}
+
+static u8 smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb)
+{
+       struct hci_conn *hcon = conn->hcon;
+       struct crypto_blkcipher *tfm = hcon->hdev->tfm;
+       int ret;
+       u8 key[16], res[16], random[16], confirm[16];
+
+       swap128(skb->data, random);
+       skb_pull(skb, sizeof(random));
+
+       memset(hcon->ltk, 0, sizeof(hcon->ltk));
+
+       if (conn->hcon->out)
+               ret = smp_c1(tfm, conn->tk, random, conn->preq, conn->prsp, 0,
+                               conn->src, conn->hcon->dst_type, conn->dst,
+                               res);
+       else
+               ret = smp_c1(tfm, conn->tk, random, conn->preq, conn->prsp,
+                               conn->hcon->dst_type, conn->dst, 0, conn->src,
+                               res);
+       if (ret)
+               return SMP_UNSPECIFIED;
+
+       BT_DBG("conn %p %s", conn, conn->hcon->out ? "master" : "slave");
+
+       swap128(res, confirm);
+
+       if (memcmp(conn->pcnf, confirm, sizeof(conn->pcnf)) != 0) {
+               BT_ERR("Pairing failed (confirmation values mismatch)");
+               return SMP_CONFIRM_FAILED;
+       }
+
+       if (conn->hcon->out) {
+               __le16 ediv;
+               u8 rand[8];
+
+               smp_s1(tfm, conn->tk, random, conn->prnd, key);
+               swap128(key, hcon->ltk);
+
+               memset(hcon->ltk + conn->smp_key_size, 0,
+                               SMP_MAX_ENC_KEY_SIZE - conn->smp_key_size);
+
+               memset(rand, 0, sizeof(rand));
+               ediv = 0;
+               hci_le_start_enc(hcon, ediv, rand, hcon->ltk);
+       } else {
+               u8 r[16];
+
+               swap128(conn->prnd, r);
+               smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(r), r);
+
+               smp_s1(tfm, conn->tk, conn->prnd, random, key);
+               swap128(key, hcon->ltk);
+
+               memset(hcon->ltk + conn->smp_key_size, 0,
+                               SMP_MAX_ENC_KEY_SIZE - conn->smp_key_size);
+       }
+
+       return 0;
+}
+
+static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb)
+{
+       struct smp_cmd_security_req *rp = (void *) skb->data;
+       struct smp_cmd_pairing cp;
+       struct hci_conn *hcon = conn->hcon;
+
+       BT_DBG("conn %p", conn);
+
+       if (test_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend))
+               return 0;
+
+       skb_pull(skb, sizeof(*rp));
+
+       memset(&cp, 0, sizeof(cp));
+       build_pairing_cmd(conn, &cp, rp->auth_req);
+
+       conn->preq[0] = SMP_CMD_PAIRING_REQ;
+       memcpy(&conn->preq[1], &cp, sizeof(cp));
+
+       smp_send_cmd(conn, SMP_CMD_PAIRING_REQ, sizeof(cp), &cp);
+
+       mod_timer(&conn->security_timer, jiffies +
+                                       msecs_to_jiffies(SMP_TIMEOUT));
+
+       set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend);
+
+       return 0;
+}
+
+int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level)
+{
+       struct hci_conn *hcon = conn->hcon;
+       __u8 authreq;
+
+       BT_DBG("conn %p hcon %p level 0x%2.2x", conn, hcon, sec_level);
+
+       if (IS_ERR(hcon->hdev->tfm))
+               return 1;
+
+       if (test_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend))
+               return 0;
+
+       if (sec_level == BT_SECURITY_LOW)
+               return 1;
+
+       if (hcon->sec_level >= sec_level)
+               return 1;
+
+       authreq = seclevel_to_authreq(sec_level);
+
+       if (hcon->link_mode & HCI_LM_MASTER) {
+               struct smp_cmd_pairing cp;
+
+               build_pairing_cmd(conn, &cp, authreq);
+               conn->preq[0] = SMP_CMD_PAIRING_REQ;
+               memcpy(&conn->preq[1], &cp, sizeof(cp));
+
+               mod_timer(&conn->security_timer, jiffies +
+                                       msecs_to_jiffies(SMP_TIMEOUT));
+
+               smp_send_cmd(conn, SMP_CMD_PAIRING_REQ, sizeof(cp), &cp);
+       } else {
+               struct smp_cmd_security_req cp;
+               cp.auth_req = authreq;
+               smp_send_cmd(conn, SMP_CMD_SECURITY_REQ, sizeof(cp), &cp);
+       }
+
+       hcon->pending_sec_level = sec_level;
+       set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend);
+
+       return 0;
+}
+
+int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb)
+{
+       __u8 code = skb->data[0];
+       __u8 reason;
+       int err = 0;
+
+       if (IS_ERR(conn->hcon->hdev->tfm)) {
+               err = PTR_ERR(conn->hcon->hdev->tfm);
+               reason = SMP_PAIRING_NOTSUPP;
+               goto done;
+       }
+
+       skb_pull(skb, sizeof(code));
+
+       switch (code) {
+       case SMP_CMD_PAIRING_REQ:
+               reason = smp_cmd_pairing_req(conn, skb);
+               break;
+
+       case SMP_CMD_PAIRING_FAIL:
+               reason = 0;
+               err = -EPERM;
+               break;
+
+       case SMP_CMD_PAIRING_RSP:
+               reason = smp_cmd_pairing_rsp(conn, skb);
+               break;
+
+       case SMP_CMD_SECURITY_REQ:
+               reason = smp_cmd_security_req(conn, skb);
+               break;
+
+       case SMP_CMD_PAIRING_CONFIRM:
+               reason = smp_cmd_pairing_confirm(conn, skb);
+               break;
+
+       case SMP_CMD_PAIRING_RANDOM:
+               reason = smp_cmd_pairing_random(conn, skb);
+               break;
+
+       case SMP_CMD_ENCRYPT_INFO:
+       case SMP_CMD_MASTER_IDENT:
+       case SMP_CMD_IDENT_INFO:
+       case SMP_CMD_IDENT_ADDR_INFO:
+       case SMP_CMD_SIGN_INFO:
+       default:
+               BT_DBG("Unknown command code 0x%2.2x", code);
+
+               reason = SMP_CMD_NOTSUPP;
+               err = -EOPNOTSUPP;
+               goto done;
+       }
+
+done:
+       if (reason)
+               smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, sizeof(reason),
+                                                               &reason);
+
+       kfree_skb(skb);
+       return err;
+}