Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ef8efe4b authored by Johan Hedberg's avatar Johan Hedberg Committed by Marcel Holtmann
Browse files

Bluetooth: Add skeleton for BR/EDR SMP channel



This patch adds the very basic code for creating and destroying SMP
L2CAP channels for BR/EDR connections.

Signed-off-by: default avatarJohan Hedberg <johan.hedberg@intel.com>
Signed-off-by: default avatarMarcel Holtmann <marcel@holtmann.org>
parent 858cdc78
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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;
+2 −0
Original line number Diff line number Diff line
@@ -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
+71 −18
Original line number Diff line number Diff line
@@ -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);
	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;

	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);
	}
}