Bluetooth: Add skeleton for BR/EDR SMP channel
authorJohan Hedberg <johan.hedberg@intel.com>
Wed, 13 Aug 2014 12:12:32 +0000 (15:12 +0300)
committerMarcel Holtmann <marcel@holtmann.org>
Wed, 3 Dec 2014 15:51:20 +0000 (16:51 +0100)
This patch adds the very basic code for creating and destroying SMP
L2CAP channels for BR/EDR connections.

Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
include/net/bluetooth/hci_core.h
include/net/bluetooth/l2cap.h
net/bluetooth/smp.c

index 42f9362a83c109390bf99c79ea30891d0cc2a15c..f39e65096b1fd70e738d97409ae0ebce623bc32c 100644 (file)
@@ -306,6 +306,7 @@ struct hci_dev {
        __u32                   req_result;
 
        void                    *smp_data;
+       void                    *smp_bredr_data;
 
        struct discovery_state  discovery;
        struct hci_conn_hash    conn_hash;
index d71dc3579354dff61d36ecc552526610da802915..eee3ef530e79310dcaa5a216c132b37aa600bec2 100644 (file)
@@ -141,6 +141,7 @@ struct l2cap_conninfo {
 #define L2CAP_FC_ATT           0x10
 #define L2CAP_FC_SIG_LE                0x20
 #define L2CAP_FC_SMP_LE                0x40
+#define L2CAP_FC_SMP_BREDR     0x80
 
 /* L2CAP Control Field bit masks */
 #define L2CAP_CTRL_SAR                 0xC000
@@ -255,6 +256,7 @@ struct l2cap_conn_rsp {
 #define L2CAP_CID_ATT          0x0004
 #define L2CAP_CID_LE_SIGNALING 0x0005
 #define L2CAP_CID_SMP          0x0006
+#define L2CAP_CID_SMP_BREDR    0x0007
 #define L2CAP_CID_DYN_START    0x0040
 #define L2CAP_CID_DYN_END      0xffff
 #define L2CAP_CID_LE_DYN_END   0x007f
index 779160485a50599929314c7ab1205f01c334149b..135e725119c5cb043bf1b3debb1633fa6d257094 100644 (file)
@@ -2504,6 +2504,9 @@ static void smp_resume_cb(struct l2cap_chan *chan)
 
        BT_DBG("chan %p", chan);
 
+       if (hcon->type == ACL_LINK)
+               return;
+
        if (!smp)
                return;
 
@@ -2527,10 +2530,14 @@ static void smp_ready_cb(struct l2cap_chan *chan)
 
 static int smp_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb)
 {
+       struct hci_conn *hcon = chan->conn->hcon;
        int err;
 
        BT_DBG("chan %p", chan);
 
+       if (hcon->type == ACL_LINK)
+               return -EOPNOTSUPP;
+
        err = smp_sig_channel(chan, skb);
        if (err) {
                struct smp_chan *smp = chan->data;
@@ -2627,34 +2634,40 @@ static const struct l2cap_ops smp_root_chan_ops = {
        .memcpy_fromiovec       = l2cap_chan_no_memcpy_fromiovec,
 };
 
-int smp_register(struct hci_dev *hdev)
+static struct l2cap_chan *smp_add_cid(struct hci_dev *hdev, u16 cid)
 {
        struct l2cap_chan *chan;
        struct crypto_blkcipher *tfm_aes;
 
-       BT_DBG("%s", hdev->name);
+       if (cid == L2CAP_CID_SMP_BREDR) {
+               tfm_aes = NULL;
+               goto create_chan;
+       }
 
        tfm_aes = crypto_alloc_blkcipher("ecb(aes)", 0, 0);
        if (IS_ERR(tfm_aes)) {
-               int err = PTR_ERR(tfm_aes);
                BT_ERR("Unable to create crypto context");
-               return err;
+               return ERR_PTR(PTR_ERR(tfm_aes));
        }
 
+create_chan:
        chan = l2cap_chan_create();
        if (!chan) {
                crypto_free_blkcipher(tfm_aes);
-               return -ENOMEM;
+               return ERR_PTR(-ENOMEM);
        }
 
        chan->data = tfm_aes;
 
-       l2cap_add_scid(chan, L2CAP_CID_SMP);
+       l2cap_add_scid(chan, cid);
 
        l2cap_chan_set_defaults(chan);
 
        bacpy(&chan->src, &hdev->bdaddr);
-       chan->src_type = BDADDR_LE_PUBLIC;
+       if (cid == L2CAP_CID_SMP)
+               chan->src_type = BDADDR_LE_PUBLIC;
+       else
+               chan->src_type = BDADDR_BREDR;
        chan->state = BT_LISTEN;
        chan->mode = L2CAP_MODE_BASIC;
        chan->imtu = L2CAP_DEFAULT_MTU;
@@ -2663,20 +2676,14 @@ int smp_register(struct hci_dev *hdev)
        /* Set correct nesting level for a parent/listening channel */
        atomic_set(&chan->nesting, L2CAP_NESTING_PARENT);
 
-       hdev->smp_data = chan;
-
-       return 0;
+       return chan;
 }
 
-void smp_unregister(struct hci_dev *hdev)
+static void smp_del_chan(struct l2cap_chan *chan)
 {
-       struct l2cap_chan *chan = hdev->smp_data;
-       struct crypto_blkcipher *tfm_aes;
-
-       if (!chan)
-               return;
+       struct crypto_blkcipher *tfm_aes;
 
-       BT_DBG("%s chan %p", hdev->name, chan);
+       BT_DBG("chan %p", chan);
 
        tfm_aes = chan->data;
        if (tfm_aes) {
@@ -2684,6 +2691,52 @@ void smp_unregister(struct hci_dev *hdev)
                crypto_free_blkcipher(tfm_aes);
        }
 
-       hdev->smp_data = NULL;
        l2cap_chan_put(chan);
 }
+
+int smp_register(struct hci_dev *hdev)
+{
+       struct l2cap_chan *chan;
+
+       BT_DBG("%s", hdev->name);
+
+       chan = smp_add_cid(hdev, L2CAP_CID_SMP);
+       if (IS_ERR(chan))
+               return PTR_ERR(chan);
+
+       hdev->smp_data = chan;
+
+       if (!lmp_sc_capable(hdev) &&
+           !test_bit(HCI_FORCE_LESC, &hdev->dbg_flags))
+               return 0;
+
+       chan = smp_add_cid(hdev, L2CAP_CID_SMP_BREDR);
+       if (IS_ERR(chan)) {
+               int err = PTR_ERR(chan);
+               chan = hdev->smp_data;
+               hdev->smp_data = NULL;
+               smp_del_chan(chan);
+               return err;
+       }
+
+       hdev->smp_bredr_data = chan;
+
+       return 0;
+}
+
+void smp_unregister(struct hci_dev *hdev)
+{
+       struct l2cap_chan *chan;
+
+       if (hdev->smp_bredr_data) {
+               chan = hdev->smp_bredr_data;
+               hdev->smp_bredr_data = NULL;
+               smp_del_chan(chan);
+       }
+
+       if (hdev->smp_data) {
+               chan = hdev->smp_data;
+               hdev->smp_data = NULL;
+               smp_del_chan(chan);
+       }
+}