|
@@ -1456,8 +1456,106 @@ int smp_distribute_keys(struct l2cap_conn *conn)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static void smp_teardown_cb(struct l2cap_chan *chan, int err)
|
|
|
+{
|
|
|
+ struct l2cap_conn *conn = chan->conn;
|
|
|
+
|
|
|
+ BT_DBG("chan %p", chan);
|
|
|
+
|
|
|
+ conn->smp = NULL;
|
|
|
+ l2cap_chan_put(chan);
|
|
|
+}
|
|
|
+
|
|
|
+static void smp_ready_cb(struct l2cap_chan *chan)
|
|
|
+{
|
|
|
+ struct l2cap_conn *conn = chan->conn;
|
|
|
+
|
|
|
+ BT_DBG("chan %p", chan);
|
|
|
+
|
|
|
+ conn->smp = chan;
|
|
|
+ l2cap_chan_hold(chan);
|
|
|
+}
|
|
|
+
|
|
|
+static struct sk_buff *smp_alloc_skb_cb(struct l2cap_chan *chan,
|
|
|
+ unsigned long hdr_len,
|
|
|
+ unsigned long len, int nb)
|
|
|
+{
|
|
|
+ struct sk_buff *skb;
|
|
|
+
|
|
|
+ skb = bt_skb_alloc(hdr_len + len, GFP_KERNEL);
|
|
|
+ if (!skb)
|
|
|
+ return ERR_PTR(-ENOMEM);
|
|
|
+
|
|
|
+ skb->priority = HCI_PRIO_MAX;
|
|
|
+ bt_cb(skb)->chan = chan;
|
|
|
+
|
|
|
+ return skb;
|
|
|
+}
|
|
|
+
|
|
|
+static const struct l2cap_ops smp_chan_ops = {
|
|
|
+ .name = "Security Manager",
|
|
|
+ .ready = smp_ready_cb,
|
|
|
+ .alloc_skb = smp_alloc_skb_cb,
|
|
|
+ .teardown = smp_teardown_cb,
|
|
|
+
|
|
|
+ .new_connection = l2cap_chan_no_new_connection,
|
|
|
+ .recv = l2cap_chan_no_recv,
|
|
|
+ .state_change = l2cap_chan_no_state_change,
|
|
|
+ .close = l2cap_chan_no_close,
|
|
|
+ .defer = l2cap_chan_no_defer,
|
|
|
+ .suspend = l2cap_chan_no_suspend,
|
|
|
+ .resume = l2cap_chan_no_resume,
|
|
|
+ .set_shutdown = l2cap_chan_no_set_shutdown,
|
|
|
+ .get_sndtimeo = l2cap_chan_no_get_sndtimeo,
|
|
|
+ .memcpy_fromiovec = l2cap_chan_no_memcpy_fromiovec,
|
|
|
+};
|
|
|
+
|
|
|
+static inline struct l2cap_chan *smp_new_conn_cb(struct l2cap_chan *pchan)
|
|
|
+{
|
|
|
+ struct l2cap_chan *chan;
|
|
|
+
|
|
|
+ BT_DBG("pchan %p", pchan);
|
|
|
+
|
|
|
+ chan = l2cap_chan_create();
|
|
|
+ if (!chan)
|
|
|
+ return NULL;
|
|
|
+
|
|
|
+ chan->chan_type = pchan->chan_type;
|
|
|
+ chan->ops = &smp_chan_ops;
|
|
|
+ chan->scid = pchan->scid;
|
|
|
+ chan->dcid = chan->scid;
|
|
|
+ chan->imtu = pchan->imtu;
|
|
|
+ chan->omtu = pchan->omtu;
|
|
|
+ chan->mode = pchan->mode;
|
|
|
+
|
|
|
+ BT_DBG("created chan %p", chan);
|
|
|
+
|
|
|
+ return chan;
|
|
|
+}
|
|
|
+
|
|
|
+static const struct l2cap_ops smp_root_chan_ops = {
|
|
|
+ .name = "Security Manager Root",
|
|
|
+ .new_connection = smp_new_conn_cb,
|
|
|
+
|
|
|
+ /* None of these are implemented for the root channel */
|
|
|
+ .close = l2cap_chan_no_close,
|
|
|
+ .alloc_skb = l2cap_chan_no_alloc_skb,
|
|
|
+ .recv = l2cap_chan_no_recv,
|
|
|
+ .state_change = l2cap_chan_no_state_change,
|
|
|
+ .teardown = l2cap_chan_no_teardown,
|
|
|
+ .ready = l2cap_chan_no_ready,
|
|
|
+ .defer = l2cap_chan_no_defer,
|
|
|
+ .suspend = l2cap_chan_no_suspend,
|
|
|
+ .resume = l2cap_chan_no_resume,
|
|
|
+ .set_shutdown = l2cap_chan_no_set_shutdown,
|
|
|
+ .get_sndtimeo = l2cap_chan_no_get_sndtimeo,
|
|
|
+ .memcpy_fromiovec = l2cap_chan_no_memcpy_fromiovec,
|
|
|
+};
|
|
|
+
|
|
|
int smp_register(struct hci_dev *hdev)
|
|
|
{
|
|
|
+ struct l2cap_chan *chan;
|
|
|
+
|
|
|
BT_DBG("%s", hdev->name);
|
|
|
|
|
|
hdev->tfm_aes = crypto_alloc_blkcipher("ecb(aes)", 0,
|
|
@@ -1469,15 +1567,46 @@ int smp_register(struct hci_dev *hdev)
|
|
|
return err;
|
|
|
}
|
|
|
|
|
|
+ chan = l2cap_chan_create();
|
|
|
+ if (!chan) {
|
|
|
+ crypto_free_blkcipher(hdev->tfm_aes);
|
|
|
+ hdev->tfm_aes = NULL;
|
|
|
+ return -ENOMEM;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* FIXME: Using reserved 0x1f value for now - to be changed to
|
|
|
+ * L2CAP_CID_SMP once all functionality is in place.
|
|
|
+ */
|
|
|
+ l2cap_add_scid(chan, 0x1f);
|
|
|
+
|
|
|
+ l2cap_chan_set_defaults(chan);
|
|
|
+
|
|
|
+ bacpy(&chan->src, &hdev->bdaddr);
|
|
|
+ chan->src_type = BDADDR_LE_PUBLIC;
|
|
|
+ chan->state = BT_LISTEN;
|
|
|
+ chan->mode = L2CAP_MODE_BASIC;
|
|
|
+ chan->imtu = L2CAP_DEFAULT_MTU;
|
|
|
+ chan->ops = &smp_root_chan_ops;
|
|
|
+
|
|
|
+ hdev->smp_data = chan;
|
|
|
+
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
void smp_unregister(struct hci_dev *hdev)
|
|
|
{
|
|
|
- BT_DBG("%s", hdev->name);
|
|
|
+ struct l2cap_chan *chan = hdev->smp_data;
|
|
|
+
|
|
|
+ if (!chan)
|
|
|
+ return;
|
|
|
+
|
|
|
+ BT_DBG("%s chan %p", hdev->name, chan);
|
|
|
|
|
|
if (hdev->tfm_aes) {
|
|
|
crypto_free_blkcipher(hdev->tfm_aes);
|
|
|
hdev->tfm_aes = NULL;
|
|
|
}
|
|
|
+
|
|
|
+ hdev->smp_data = NULL;
|
|
|
+ l2cap_chan_put(chan);
|
|
|
}
|