diff options
author | Johan Hedberg <johan.hedberg@intel.com> | 2013-05-14 06:27:21 -0400 |
---|---|---|
committer | Marcel Holtmann <marcel@holtmann.org> | 2013-12-05 10:05:33 -0500 |
commit | 27e2d4c8d28be1d1b4ecfbffab572d7dbd35254d (patch) | |
tree | 08d2b9dd538a3c410cfb2e6648048ae54766a7bc /net | |
parent | 791d60f71a8d743df17a5029bb080a24afc4fff6 (diff) |
Bluetooth: Add basic LE L2CAP connect request receiving support
This patch adds the necessary boiler plate code to handle receiving
L2CAP connect requests over LE and respond to them with a proper connect
response.
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
Diffstat (limited to 'net')
-rw-r--r-- | net/bluetooth/l2cap_core.c | 152 |
1 files changed, 152 insertions, 0 deletions
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 5f9287fd86df..d6f518d7e61b 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c | |||
@@ -617,6 +617,29 @@ void l2cap_chan_del(struct l2cap_chan *chan, int err) | |||
617 | return; | 617 | return; |
618 | } | 618 | } |
619 | 619 | ||
620 | static void l2cap_chan_le_connect_reject(struct l2cap_chan *chan) | ||
621 | { | ||
622 | struct l2cap_conn *conn = chan->conn; | ||
623 | struct l2cap_le_conn_rsp rsp; | ||
624 | u16 result; | ||
625 | |||
626 | if (test_bit(FLAG_DEFER_SETUP, &chan->flags)) | ||
627 | result = L2CAP_CR_AUTHORIZATION; | ||
628 | else | ||
629 | result = L2CAP_CR_BAD_PSM; | ||
630 | |||
631 | l2cap_state_change(chan, BT_DISCONN); | ||
632 | |||
633 | rsp.dcid = cpu_to_le16(chan->scid); | ||
634 | rsp.mtu = cpu_to_le16(chan->imtu); | ||
635 | rsp.mps = __constant_cpu_to_le16(L2CAP_LE_DEFAULT_MPS); | ||
636 | rsp.credits = __constant_cpu_to_le16(L2CAP_LE_MAX_CREDITS); | ||
637 | rsp.result = cpu_to_le16(result); | ||
638 | |||
639 | l2cap_send_cmd(conn, chan->ident, L2CAP_LE_CONN_RSP, sizeof(rsp), | ||
640 | &rsp); | ||
641 | } | ||
642 | |||
620 | static void l2cap_chan_connect_reject(struct l2cap_chan *chan) | 643 | static void l2cap_chan_connect_reject(struct l2cap_chan *chan) |
621 | { | 644 | { |
622 | struct l2cap_conn *conn = chan->conn; | 645 | struct l2cap_conn *conn = chan->conn; |
@@ -663,6 +686,8 @@ void l2cap_chan_close(struct l2cap_chan *chan, int reason) | |||
663 | if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED) { | 686 | if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED) { |
664 | if (conn->hcon->type == ACL_LINK) | 687 | if (conn->hcon->type == ACL_LINK) |
665 | l2cap_chan_connect_reject(chan); | 688 | l2cap_chan_connect_reject(chan); |
689 | else if (conn->hcon->type == LE_LINK) | ||
690 | l2cap_chan_le_connect_reject(chan); | ||
666 | } | 691 | } |
667 | 692 | ||
668 | l2cap_chan_del(chan, reason); | 693 | l2cap_chan_del(chan, reason); |
@@ -3641,6 +3666,23 @@ static int l2cap_build_conf_rsp(struct l2cap_chan *chan, void *data, | |||
3641 | return ptr - data; | 3666 | return ptr - data; |
3642 | } | 3667 | } |
3643 | 3668 | ||
3669 | void __l2cap_le_connect_rsp_defer(struct l2cap_chan *chan) | ||
3670 | { | ||
3671 | struct l2cap_le_conn_rsp rsp; | ||
3672 | struct l2cap_conn *conn = chan->conn; | ||
3673 | |||
3674 | BT_DBG("chan %p", chan); | ||
3675 | |||
3676 | rsp.dcid = cpu_to_le16(chan->scid); | ||
3677 | rsp.mtu = cpu_to_le16(chan->imtu); | ||
3678 | rsp.mps = __constant_cpu_to_le16(L2CAP_LE_DEFAULT_MPS); | ||
3679 | rsp.credits = __constant_cpu_to_le16(L2CAP_LE_MAX_CREDITS); | ||
3680 | rsp.result = __constant_cpu_to_le16(L2CAP_CR_SUCCESS); | ||
3681 | |||
3682 | l2cap_send_cmd(conn, chan->ident, L2CAP_LE_CONN_RSP, sizeof(rsp), | ||
3683 | &rsp); | ||
3684 | } | ||
3685 | |||
3644 | void __l2cap_connect_rsp_defer(struct l2cap_chan *chan) | 3686 | void __l2cap_connect_rsp_defer(struct l2cap_chan *chan) |
3645 | { | 3687 | { |
3646 | struct l2cap_conn_rsp rsp; | 3688 | struct l2cap_conn_rsp rsp; |
@@ -5382,6 +5424,113 @@ static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn, | |||
5382 | return err; | 5424 | return err; |
5383 | } | 5425 | } |
5384 | 5426 | ||
5427 | static int l2cap_le_connect_req(struct l2cap_conn *conn, | ||
5428 | struct l2cap_cmd_hdr *cmd, u16 cmd_len, | ||
5429 | u8 *data) | ||
5430 | { | ||
5431 | struct l2cap_le_conn_req *req = (struct l2cap_le_conn_req *) data; | ||
5432 | struct l2cap_le_conn_rsp rsp; | ||
5433 | struct l2cap_chan *chan, *pchan; | ||
5434 | u16 dcid, scid, mtu, mps; | ||
5435 | __le16 psm; | ||
5436 | u8 result; | ||
5437 | |||
5438 | if (cmd_len != sizeof(*req)) | ||
5439 | return -EPROTO; | ||
5440 | |||
5441 | scid = __le16_to_cpu(req->scid); | ||
5442 | mtu = __le16_to_cpu(req->mtu); | ||
5443 | mps = __le16_to_cpu(req->mps); | ||
5444 | psm = req->psm; | ||
5445 | dcid = 0; | ||
5446 | |||
5447 | if (mtu < 23 || mps < 23) | ||
5448 | return -EPROTO; | ||
5449 | |||
5450 | BT_DBG("psm 0x%2.2x scid 0x%4.4x mtu %u mps %u", __le16_to_cpu(psm), | ||
5451 | scid, mtu, mps); | ||
5452 | |||
5453 | /* Check if we have socket listening on psm */ | ||
5454 | pchan = l2cap_global_chan_by_psm(BT_LISTEN, psm, &conn->hcon->src, | ||
5455 | &conn->hcon->dst, LE_LINK); | ||
5456 | if (!pchan) { | ||
5457 | result = L2CAP_CR_BAD_PSM; | ||
5458 | chan = NULL; | ||
5459 | goto response; | ||
5460 | } | ||
5461 | |||
5462 | mutex_lock(&conn->chan_lock); | ||
5463 | l2cap_chan_lock(pchan); | ||
5464 | |||
5465 | if (!smp_sufficient_security(conn->hcon, pchan->sec_level)) { | ||
5466 | result = L2CAP_CR_AUTHENTICATION; | ||
5467 | chan = NULL; | ||
5468 | goto response_unlock; | ||
5469 | } | ||
5470 | |||
5471 | /* Check if we already have channel with that dcid */ | ||
5472 | if (__l2cap_get_chan_by_dcid(conn, scid)) { | ||
5473 | result = L2CAP_CR_NO_MEM; | ||
5474 | chan = NULL; | ||
5475 | goto response_unlock; | ||
5476 | } | ||
5477 | |||
5478 | chan = pchan->ops->new_connection(pchan); | ||
5479 | if (!chan) { | ||
5480 | result = L2CAP_CR_NO_MEM; | ||
5481 | goto response_unlock; | ||
5482 | } | ||
5483 | |||
5484 | bacpy(&chan->src, &conn->hcon->src); | ||
5485 | bacpy(&chan->dst, &conn->hcon->dst); | ||
5486 | chan->src_type = bdaddr_type(conn->hcon, conn->hcon->src_type); | ||
5487 | chan->dst_type = bdaddr_type(conn->hcon, conn->hcon->dst_type); | ||
5488 | chan->psm = psm; | ||
5489 | chan->dcid = scid; | ||
5490 | chan->omtu = mtu; | ||
5491 | chan->remote_mps = mps; | ||
5492 | |||
5493 | __l2cap_chan_add(conn, chan); | ||
5494 | dcid = chan->scid; | ||
5495 | |||
5496 | __set_chan_timer(chan, chan->ops->get_sndtimeo(chan)); | ||
5497 | |||
5498 | chan->ident = cmd->ident; | ||
5499 | |||
5500 | if (test_bit(FLAG_DEFER_SETUP, &chan->flags)) { | ||
5501 | l2cap_state_change(chan, BT_CONNECT2); | ||
5502 | result = L2CAP_CR_PEND; | ||
5503 | chan->ops->defer(chan); | ||
5504 | } else { | ||
5505 | l2cap_chan_ready(chan); | ||
5506 | result = L2CAP_CR_SUCCESS; | ||
5507 | } | ||
5508 | |||
5509 | response_unlock: | ||
5510 | l2cap_chan_unlock(pchan); | ||
5511 | mutex_unlock(&conn->chan_lock); | ||
5512 | |||
5513 | if (result == L2CAP_CR_PEND) | ||
5514 | return 0; | ||
5515 | |||
5516 | response: | ||
5517 | if (chan) { | ||
5518 | rsp.mtu = cpu_to_le16(chan->imtu); | ||
5519 | rsp.mps = __constant_cpu_to_le16(L2CAP_LE_DEFAULT_MPS); | ||
5520 | } else { | ||
5521 | rsp.mtu = 0; | ||
5522 | rsp.mps = 0; | ||
5523 | } | ||
5524 | |||
5525 | rsp.dcid = cpu_to_le16(dcid); | ||
5526 | rsp.credits = __constant_cpu_to_le16(L2CAP_LE_MAX_CREDITS); | ||
5527 | rsp.result = cpu_to_le16(result); | ||
5528 | |||
5529 | l2cap_send_cmd(conn, cmd->ident, L2CAP_LE_CONN_RSP, sizeof(rsp), &rsp); | ||
5530 | |||
5531 | return 0; | ||
5532 | } | ||
5533 | |||
5385 | static inline int l2cap_le_sig_cmd(struct l2cap_conn *conn, | 5534 | static inline int l2cap_le_sig_cmd(struct l2cap_conn *conn, |
5386 | struct l2cap_cmd_hdr *cmd, u16 cmd_len, | 5535 | struct l2cap_cmd_hdr *cmd, u16 cmd_len, |
5387 | u8 *data) | 5536 | u8 *data) |
@@ -5400,6 +5549,9 @@ static inline int l2cap_le_sig_cmd(struct l2cap_conn *conn, | |||
5400 | l2cap_le_connect_rsp(conn, cmd, cmd_len, data); | 5549 | l2cap_le_connect_rsp(conn, cmd, cmd_len, data); |
5401 | return 0; | 5550 | return 0; |
5402 | 5551 | ||
5552 | case L2CAP_LE_CONN_REQ: | ||
5553 | return l2cap_le_connect_req(conn, cmd, cmd_len, data); | ||
5554 | |||
5403 | default: | 5555 | default: |
5404 | BT_ERR("Unknown LE signaling command 0x%2.2x", cmd->code); | 5556 | BT_ERR("Unknown LE signaling command 0x%2.2x", cmd->code); |
5405 | return -EINVAL; | 5557 | return -EINVAL; |