Hi,
This is an implementation of the Just Works SMP procedure, on top of the work
done by Ville (included in this series to give some context). The SMP stuff
starts at 11/20.
The most important thing about this series is the discussion that (I hope)
it will cause.
Some things that I would like to point (in no order):
- the SMP function names follow the spec nomeclature, would it be better to
use more meaningful names?
- the crypto transform is allocated during the adapter registration, is this
the best place to do this?
- renaming l2cap.c to l2cap_core.c was the only way we could find to keep the
SMP implementation separated from the "core" l2cap.
I think this is enough to start a discussion.
So, as the Human Torch would say: Flame on! :-)
Anderson Briglia (3):
Bluetooth: Start SMP procedure
Bluetooth: simple SMP pairing negotiation
Bluetooth: LE SMP Cryptoolbox functions
Ville Tervo (10):
Bluetooth: Add low energy commands and events
Bluetooth: Add LE connect support
Bluetooth: Use LE buffers for LE traffic
Bluetooth: Add LE connection support to L2CAP
Bluetooth: Add server socket support for LE connection
Bluetooth: Do not send disconn comand over LE links
Bluetooth: Treat LE and ACL links separately on timeout
Bluetooth: Fix locking balance in l2cap_le_conn_ready
Bluetooth: Add server socket support for LE connection
Bluetooth: Add SMP command structures
Vinicius Costa Gomes (7):
Bluetooth: Fix initiated LE connections
Bluetooth: fix receiving L2CAP packets over LE
Bluetooth: Implement the first SMP commands
Bluetooth: Add support for using the crypto subsystem
Bluetooth: Add support for SMP confirmation checks
Bluetooth: Add support for LE Start Encryption
Bluetooth: Add support for resuming socket when SMP is finished
include/net/bluetooth/hci.h | 86 ++++++
include/net/bluetooth/hci_core.h | 37 +++-
include/net/bluetooth/l2cap.h | 10 +
include/net/bluetooth/smp.h | 80 ++++++
net/bluetooth/Makefile | 1 +
net/bluetooth/hci_conn.c | 104 +++++++-
net/bluetooth/hci_core.c | 94 ++++++-
net/bluetooth/hci_event.c | 206 ++++++++++++++-
net/bluetooth/{l2cap.c => l2cap_core.c} | 237 +++++++++++++---
net/bluetooth/smp.c | 465 +++++++++++++++++++++++++++++++
10 files changed, 1264 insertions(+), 56 deletions(-)
create mode 100644 include/net/bluetooth/smp.h
rename net/bluetooth/{l2cap.c => l2cap_core.c} (96%)
create mode 100644 net/bluetooth/smp.c
--
1.7.3.2
Hi Ville,
* Ville Tervo <[email protected]> [2010-11-24 07:42:11 +0200]:
> Hi,
>
>
> On Tue, Nov 23, 2010 at 12:06:16PM -0300, ext Vinicius Costa Gomes wrote:
> > Hi,
> >
> > This is an implementation of the Just Works SMP procedure, on top of the work
> > done by Ville (included in this series to give some context). The SMP stuff
> > starts at 11/20.
> >
> > The most important thing about this series is the discussion that (I hope)
> > it will cause.
> >
> > Some things that I would like to point (in no order):
> >
> > - the SMP function names follow the spec nomeclature, would it be better to
> > use more meaningful names?
> >
> > - the crypto transform is allocated during the adapter registration, is this
> > the best place to do this?
> >
> > - renaming l2cap.c to l2cap_core.c was the only way we could find to keep the
> > SMP implementation separated from the "core" l2cap.
>
> Gustavo do you have something against this change?
I'm ok with this change.
--
Gustavo F. Padovan
http://profusion.mobi
Hi Ville,
On 07:32 Wed 24 Nov, Ville Tervo wrote:
> Hi,
>
>
> On Tue, Nov 23, 2010 at 12:06:28PM -0300, ext Vinicius Costa Gomes wrote:
> > As L2CAP packets coming over LE don't have any more encapsulation,
> > other than L2CAP, we are able to process them as soon as they arrive.
> >
>
> I thought this patch was only fixing symphoms of broken controller? LE frames
> should have same continuation bits as ACL. See Vol 2 Part E 5.4.2.
You are right. Broken controller. A better behaved one has the right flags.
Will drop this patch.
>
> > Signed-off-by: Vinicius Costa Gomes <[email protected]>
> > ---
> > net/bluetooth/l2cap.c | 17 +++++++++++++++--
> > 1 files changed, 15 insertions(+), 2 deletions(-)
> >
> > diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c
> > index e481d6b..0d168aa 100644
> > --- a/net/bluetooth/l2cap.c
> > +++ b/net/bluetooth/l2cap.c
> > @@ -4798,17 +4798,30 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
> > static int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags)
> > {
> > struct l2cap_conn *conn = hcon->l2cap_data;
> > + struct l2cap_hdr *hdr;
> > + int len;
> >
> > if (!conn && !(conn = l2cap_conn_add(hcon, 0)))
> > goto drop;
> >
> > BT_DBG("conn %p len %d flags 0x%x", conn, skb->len, flags);
> >
> > + if (hcon->type == LE_LINK) {
> > + hdr = (struct l2cap_hdr *) skb->data;
> > + len = __le16_to_cpu(hdr->len) + L2CAP_HDR_SIZE;
> > +
> > + if (len == skb->len) {
> > + /* Complete frame received */
> > + l2cap_recv_frame(conn, skb);
> > + return 0;
> > + }
> > +
> > + goto drop;
> > + }
> > +
> > if (flags & ACL_START) {
> > - struct l2cap_hdr *hdr;
> > struct sock *sk;
> > u16 cid;
> > - int len;
> >
> > if (conn->rx_len) {
> > BT_ERR("Unexpected start frame (len %d)", skb->len);
> > --
> > 1.7.3.2
> >
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
> > the body of a message to [email protected]
> > More majordomo info at http://vger.kernel.org/majordomo-info.html
Cheers,
--
Vinicius
Hi,
On Tue, Nov 23, 2010 at 12:06:16PM -0300, ext Vinicius Costa Gomes wrote:
> Hi,
>
> This is an implementation of the Just Works SMP procedure, on top of the work
> done by Ville (included in this series to give some context). The SMP stuff
> starts at 11/20.
>
> The most important thing about this series is the discussion that (I hope)
> it will cause.
>
> Some things that I would like to point (in no order):
>
> - the SMP function names follow the spec nomeclature, would it be better to
> use more meaningful names?
>
> - the crypto transform is allocated during the adapter registration, is this
> the best place to do this?
>
> - renaming l2cap.c to l2cap_core.c was the only way we could find to keep the
> SMP implementation separated from the "core" l2cap.
Gustavo do you have something against this change?
>
> I think this is enough to start a discussion.
>
> So, as the Human Torch would say: Flame on! :-)
I'll try to find time to do some testing soon.
>
> Anderson Briglia (3):
> Bluetooth: Start SMP procedure
> Bluetooth: simple SMP pairing negotiation
> Bluetooth: LE SMP Cryptoolbox functions
>
> Ville Tervo (10):
> Bluetooth: Add low energy commands and events
> Bluetooth: Add LE connect support
> Bluetooth: Use LE buffers for LE traffic
> Bluetooth: Add LE connection support to L2CAP
> Bluetooth: Add server socket support for LE connection
> Bluetooth: Do not send disconn comand over LE links
> Bluetooth: Treat LE and ACL links separately on timeout
> Bluetooth: Fix locking balance in l2cap_le_conn_ready
> Bluetooth: Add server socket support for LE connection
> Bluetooth: Add SMP command structures
>
> Vinicius Costa Gomes (7):
> Bluetooth: Fix initiated LE connections
> Bluetooth: fix receiving L2CAP packets over LE
> Bluetooth: Implement the first SMP commands
> Bluetooth: Add support for using the crypto subsystem
> Bluetooth: Add support for SMP confirmation checks
> Bluetooth: Add support for LE Start Encryption
> Bluetooth: Add support for resuming socket when SMP is finished
>
> include/net/bluetooth/hci.h | 86 ++++++
> include/net/bluetooth/hci_core.h | 37 +++-
> include/net/bluetooth/l2cap.h | 10 +
> include/net/bluetooth/smp.h | 80 ++++++
> net/bluetooth/Makefile | 1 +
> net/bluetooth/hci_conn.c | 104 +++++++-
> net/bluetooth/hci_core.c | 94 ++++++-
> net/bluetooth/hci_event.c | 206 ++++++++++++++-
> net/bluetooth/{l2cap.c => l2cap_core.c} | 237 +++++++++++++---
> net/bluetooth/smp.c | 465 +++++++++++++++++++++++++++++++
> 10 files changed, 1264 insertions(+), 56 deletions(-)
> create mode 100644 include/net/bluetooth/smp.h
> rename net/bluetooth/{l2cap.c => l2cap_core.c} (96%)
> create mode 100644 net/bluetooth/smp.c
>
> --
> 1.7.3.2
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html
Hi,
On Tue, Nov 23, 2010 at 12:06:28PM -0300, ext Vinicius Costa Gomes wrote:
> As L2CAP packets coming over LE don't have any more encapsulation,
> other than L2CAP, we are able to process them as soon as they arrive.
>
I thought this patch was only fixing symphoms of broken controller? LE frames
should have same continuation bits as ACL. See Vol 2 Part E 5.4.2.
> Signed-off-by: Vinicius Costa Gomes <[email protected]>
> ---
> net/bluetooth/l2cap.c | 17 +++++++++++++++--
> 1 files changed, 15 insertions(+), 2 deletions(-)
>
> diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c
> index e481d6b..0d168aa 100644
> --- a/net/bluetooth/l2cap.c
> +++ b/net/bluetooth/l2cap.c
> @@ -4798,17 +4798,30 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
> static int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags)
> {
> struct l2cap_conn *conn = hcon->l2cap_data;
> + struct l2cap_hdr *hdr;
> + int len;
>
> if (!conn && !(conn = l2cap_conn_add(hcon, 0)))
> goto drop;
>
> BT_DBG("conn %p len %d flags 0x%x", conn, skb->len, flags);
>
> + if (hcon->type == LE_LINK) {
> + hdr = (struct l2cap_hdr *) skb->data;
> + len = __le16_to_cpu(hdr->len) + L2CAP_HDR_SIZE;
> +
> + if (len == skb->len) {
> + /* Complete frame received */
> + l2cap_recv_frame(conn, skb);
> + return 0;
> + }
> +
> + goto drop;
> + }
> +
> if (flags & ACL_START) {
> - struct l2cap_hdr *hdr;
> struct sock *sk;
> u16 cid;
> - int len;
>
> if (conn->rx_len) {
> BT_ERR("Unexpected start frame (len %d)", skb->len);
> --
> 1.7.3.2
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html
On Tue, Nov 23, 2010 at 08:48:39PM -0200, ext Gustavo F. Padovan wrote:
> * Vinicius Costa Gomes <[email protected]> [2010-11-23 12:06:22 -0300]:
>
> > From: Ville Tervo <[email protected]>
> >
> > l2cap over LE links can be disconnected without sending
> > disconnect command first.
> >
> > Signed-off-by: Ville Tervo <[email protected]>
> > ---
> > net/bluetooth/l2cap.c | 15 ++++++++++-----
> > 1 files changed, 10 insertions(+), 5 deletions(-)
> >
> > diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c
> > index 0b583c4..5894662 100644
> > --- a/net/bluetooth/l2cap.c
> > +++ b/net/bluetooth/l2cap.c
> > @@ -873,6 +873,8 @@ static void l2cap_sock_kill(struct sock *sk)
> >
> > static void __l2cap_sock_close(struct sock *sk, int reason)
> > {
> > + struct l2cap_conn *conn;
> > +
> > BT_DBG("sk %p state %d socket %p", sk, sk->sk_state, sk->sk_socket);
> >
> > switch (sk->sk_state) {
> > @@ -882,8 +884,10 @@ static void __l2cap_sock_close(struct sock *sk, int reason)
> >
> > case BT_CONNECTED:
> > case BT_CONFIG:
> > - if (sk->sk_type == SOCK_SEQPACKET ||
> > - sk->sk_type == SOCK_STREAM) {
> > + conn = l2cap_pi(sk)->conn;
> > + if ((sk->sk_type == SOCK_SEQPACKET ||
> > + sk->sk_type == SOCK_STREAM) &&
> > + conn->hcon->type != LE_LINK) {
>
> Makes more senses check for conn->hcon->type == ACL_LINK.
Will be changed.
>
> > struct l2cap_conn *conn = l2cap_pi(sk)->conn;
> >
> > l2cap_sock_set_timer(sk, sk->sk_sndtimeo);
> > @@ -893,9 +897,10 @@ static void __l2cap_sock_close(struct sock *sk, int reason)
> > break;
> >
> > case BT_CONNECT2:
> > - if (sk->sk_type == SOCK_SEQPACKET ||
> > - sk->sk_type == SOCK_STREAM) {
> > - struct l2cap_conn *conn = l2cap_pi(sk)->conn;
> > + conn = l2cap_pi(sk)->conn;
> > + if ((sk->sk_type == SOCK_SEQPACKET ||
> > + sk->sk_type == SOCK_STREAM) &&
> > + conn->hcon->type != LE_LINK) {
>
> Same here.
Ditto
--
Ville
On Tue, Nov 23, 2010 at 03:57:05PM -0200, ext Gustavo F. Padovan wrote:
> Hi Ville,
>
> * Vinicius Costa Gomes <[email protected]> [2010-11-23 12:06:18 -0300]:
>
> > From: Ville Tervo <[email protected]>
> >
> > Bluetooth V4.0 adds support for Low Energy (LE)
> > connections. Specification introduses new set
> > of hci commands to control LE connection.
> > This patch adds logic to create, cancel and
> > disconnect LE connections
> >
> > Signed-off-by: Ville Tervo <[email protected]>
>
> I remember Marcel acked some of these patches. Why the ack is not here?
> It makes life easier if you tell who is acking your patch already.
There was more comments which changed the patch after acking.
--
Ville
Hi Gustavo,
On 20:55 Tue 23 Nov , Gustavo F. Padovan wrote:
> Hi Vinicius,
>
> * Vinicius Costa Gomes <[email protected]> [2010-11-23 12:06:28 -0300]:
>
> > As L2CAP packets coming over LE don't have any more encapsulation,
> > other than L2CAP, we are able to process them as soon as they arrive.
> >
> > Signed-off-by: Vinicius Costa Gomes <[email protected]>
> > ---
> > net/bluetooth/l2cap.c | 17 +++++++++++++++--
> > 1 files changed, 15 insertions(+), 2 deletions(-)
>
> So move this patch before the SMP changes in the patchset.
Will do.
>
> --
> Gustavo F. Padovan
> http://profusion.mobi
Cheers,
--
Vinicius
On 20:54 Tue 23 Nov , Gustavo F. Padovan wrote:
> * Vinicius Costa Gomes <[email protected]> [2010-11-23 12:06:26 -0300]:
>
> > From: Ville Tervo <[email protected]>
> >
> > Add support for LE server sockets.
> >
> > Signed-off-by: Ville Tervo <[email protected]>
> > ---
> > net/bluetooth/l2cap.c | 3 +--
> > 1 files changed, 1 insertions(+), 2 deletions(-)
> >
> > diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c
> > index 172bf93..e481d6b 100644
> > --- a/net/bluetooth/l2cap.c
> > +++ b/net/bluetooth/l2cap.c
> > @@ -1438,8 +1438,7 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn)
> > BT_DBG("");
> >
> > /* Check if we have socket listening on cid */
> > - parent = l2cap_get_sock_by_cid(BT_LISTEN, L2CAP_CID_LE_DATA,
> > - conn->src);
> > + parent = l2cap_get_sock_by_cid(BT_LISTEN, L2CAP_CID_LE_DATA, conn->src);
>
> I didn't get this change, commit message tell about other stuff.
My mistake, this should have happened when I rebased these changes on top of
bluetooth-next.
>
> --
> Gustavo F. Padovan
> http://profusion.mobi
Cheers,
--
Vinicius
Hi Vinicius,
* Vinicius Costa Gomes <[email protected]> [2010-11-23 12:06:33 -0300]:
> This will allow using the crypto subsystem for encrypting data. As SMP
> (Security Manager Protocol) is implemented almost entirely on the host
> side and the crypto module already implements the needed methods
> (AES-128), it makes sense to use it.
>
> Signed-off-by: Vinicius Costa Gomes <[email protected]>
> ---
> include/net/bluetooth/hci_core.h | 2 ++
> net/bluetooth/hci_core.c | 10 ++++++++++
> net/bluetooth/smp.c | 1 -
> 3 files changed, 12 insertions(+), 1 deletions(-)
>
> diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
> index e2d857a..ee5efa8 100644
> --- a/include/net/bluetooth/hci_core.h
> +++ b/include/net/bluetooth/hci_core.h
> @@ -135,6 +135,8 @@ struct hci_dev {
> __u32 req_status;
> __u32 req_result;
>
> + struct crypto_blkcipher *tfm;
> +
> struct inquiry_cache inq_cache;
> struct hci_conn_hash conn_hash;
> struct list_head blacklist;
> diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
> index 5750b80..0e98ffb 100644
> --- a/net/bluetooth/hci_core.c
> +++ b/net/bluetooth/hci_core.c
> @@ -41,6 +41,7 @@
> #include <linux/interrupt.h>
> #include <linux/notifier.h>
> #include <linux/rfkill.h>
> +#include <linux/crypto.h>
> #include <net/sock.h>
>
> #include <asm/system.h>
> @@ -947,6 +948,13 @@ int hci_register_dev(struct hci_dev *hdev)
> if (!hdev->workqueue)
> goto nomem;
>
> + hdev->tfm = crypto_alloc_blkcipher("ecb(aes)", 0, CRYPTO_ALG_ASYNC);
> + if (IS_ERR(hdev->tfm)) {
> + BT_ERR("Failed to load transform for ecb(aes): %ld",
> + PTR_ERR(hdev->tfm));
> + goto nomem;
> + }
> +
> hci_register_sysfs(hdev);
>
> hdev->rfkill = rfkill_alloc(hdev->name, &hdev->dev,
> @@ -987,6 +995,8 @@ int hci_unregister_dev(struct hci_dev *hdev)
> for (i = 0; i < NUM_REASSEMBLY; i++)
> kfree_skb(hdev->reassembly[i]);
>
> + crypto_free_blkcipher(hdev->tfm);
> +
> hci_notify(hdev, HCI_DEV_UNREG);
>
> if (hdev->rfkill) {
> diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c
> index 7997978..8e8be47 100644
> --- a/net/bluetooth/smp.c
> +++ b/net/bluetooth/smp.c
> @@ -227,7 +227,6 @@ static void smp_cmd_pairing_confirm(struct l2cap_conn *conn, struct sk_buff *skb
> BT_DBG("");
>
> if (conn->hcon->out) {
> -
Change not related to your patch here.
--
Gustavo F. Padovan
http://profusion.mobi
Hi Vinicius,
* Vinicius Costa Gomes <[email protected]> [2010-11-23 12:06:28 -0300]:
> As L2CAP packets coming over LE don't have any more encapsulation,
> other than L2CAP, we are able to process them as soon as they arrive.
>
> Signed-off-by: Vinicius Costa Gomes <[email protected]>
> ---
> net/bluetooth/l2cap.c | 17 +++++++++++++++--
> 1 files changed, 15 insertions(+), 2 deletions(-)
So move this patch before the SMP changes in the patchset.
--
Gustavo F. Padovan
http://profusion.mobi
* Vinicius Costa Gomes <[email protected]> [2010-11-23 12:06:26 -0300]:
> From: Ville Tervo <[email protected]>
>
> Add support for LE server sockets.
>
> Signed-off-by: Ville Tervo <[email protected]>
> ---
> net/bluetooth/l2cap.c | 3 +--
> 1 files changed, 1 insertions(+), 2 deletions(-)
>
> diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c
> index 172bf93..e481d6b 100644
> --- a/net/bluetooth/l2cap.c
> +++ b/net/bluetooth/l2cap.c
> @@ -1438,8 +1438,7 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn)
> BT_DBG("");
>
> /* Check if we have socket listening on cid */
> - parent = l2cap_get_sock_by_cid(BT_LISTEN, L2CAP_CID_LE_DATA,
> - conn->src);
> + parent = l2cap_get_sock_by_cid(BT_LISTEN, L2CAP_CID_LE_DATA, conn->src);
I didn't get this change, commit message tell about other stuff.
--
Gustavo F. Padovan
http://profusion.mobi
Hi Ville,
* Vinicius Costa Gomes <[email protected]> [2010-11-23 12:06:25 -0300]:
> From: Ville Tervo <[email protected]>
>
> Don't try to unlock sock if the lock was not held.
>
> Signed-off-by: Ville Tervo <[email protected]>
> ---
> net/bluetooth/l2cap.c | 2 +-
> 1 files changed, 1 insertions(+), 1 deletions(-)
>
> diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c
> index 5894662..172bf93 100644
> --- a/net/bluetooth/l2cap.c
> +++ b/net/bluetooth/l2cap.c
> @@ -1441,7 +1441,7 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn)
> parent = l2cap_get_sock_by_cid(BT_LISTEN, L2CAP_CID_LE_DATA,
> conn->src);
> if (!parent)
> - goto clean;
> + return;
Why not merging this into patch 5/20?
--
Gustavo F. Padovan
http://profusion.mobi
* Vinicius Costa Gomes <[email protected]> [2010-11-23 12:06:22 -0300]:
> From: Ville Tervo <[email protected]>
>
> l2cap over LE links can be disconnected without sending
> disconnect command first.
>
> Signed-off-by: Ville Tervo <[email protected]>
> ---
> net/bluetooth/l2cap.c | 15 ++++++++++-----
> 1 files changed, 10 insertions(+), 5 deletions(-)
>
> diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c
> index 0b583c4..5894662 100644
> --- a/net/bluetooth/l2cap.c
> +++ b/net/bluetooth/l2cap.c
> @@ -873,6 +873,8 @@ static void l2cap_sock_kill(struct sock *sk)
>
> static void __l2cap_sock_close(struct sock *sk, int reason)
> {
> + struct l2cap_conn *conn;
> +
> BT_DBG("sk %p state %d socket %p", sk, sk->sk_state, sk->sk_socket);
>
> switch (sk->sk_state) {
> @@ -882,8 +884,10 @@ static void __l2cap_sock_close(struct sock *sk, int reason)
>
> case BT_CONNECTED:
> case BT_CONFIG:
> - if (sk->sk_type == SOCK_SEQPACKET ||
> - sk->sk_type == SOCK_STREAM) {
> + conn = l2cap_pi(sk)->conn;
> + if ((sk->sk_type == SOCK_SEQPACKET ||
> + sk->sk_type == SOCK_STREAM) &&
> + conn->hcon->type != LE_LINK) {
Makes more senses check for conn->hcon->type == ACL_LINK.
> struct l2cap_conn *conn = l2cap_pi(sk)->conn;
>
> l2cap_sock_set_timer(sk, sk->sk_sndtimeo);
> @@ -893,9 +897,10 @@ static void __l2cap_sock_close(struct sock *sk, int reason)
> break;
>
> case BT_CONNECT2:
> - if (sk->sk_type == SOCK_SEQPACKET ||
> - sk->sk_type == SOCK_STREAM) {
> - struct l2cap_conn *conn = l2cap_pi(sk)->conn;
> + conn = l2cap_pi(sk)->conn;
> + if ((sk->sk_type == SOCK_SEQPACKET ||
> + sk->sk_type == SOCK_STREAM) &&
> + conn->hcon->type != LE_LINK) {
Same here.
--
Gustavo F. Padovan
http://profusion.mobi
Hi Ville,
* Vinicius Costa Gomes <[email protected]> [2010-11-23 12:06:18 -0300]:
> From: Ville Tervo <[email protected]>
>
> Bluetooth V4.0 adds support for Low Energy (LE)
> connections. Specification introduses new set
> of hci commands to control LE connection.
> This patch adds logic to create, cancel and
> disconnect LE connections
>
> Signed-off-by: Ville Tervo <[email protected]>
I remember Marcel acked some of these patches. Why the ack is not here?
It makes life easier if you tell who is acking your patch already.
--
Gustavo F. Padovan
http://profusion.mobi
This adds support for resuming the user space traffic when SMP
negotiation is complete.
Signed-off-by: Vinicius Costa Gomes <[email protected]>
---
include/net/bluetooth/l2cap.h | 1 +
net/bluetooth/l2cap_core.c | 86 +++++++++++++++++++++++++++--------------
2 files changed, 58 insertions(+), 29 deletions(-)
diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
index 1d0555f..196ad4e 100644
--- a/include/net/bluetooth/l2cap.h
+++ b/include/net/bluetooth/l2cap.h
@@ -433,5 +433,6 @@ static inline int l2cap_tx_window_full(struct sock *sk)
#define __is_sar_start(ctrl) ((ctrl) & L2CAP_CTRL_SAR) == L2CAP_SDU_START
void l2cap_load(void);
+void l2cap_le_do_start(struct l2cap_conn *conn);
#endif /* __L2CAP_H */
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 24719d3..df5402b 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -624,6 +624,31 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
}
}
+static void l2cap_chan_ready(struct sock *sk)
+{
+ struct sock *parent = bt_sk(sk)->parent;
+
+ BT_DBG("sk %p, parent %p", sk, parent);
+
+ l2cap_pi(sk)->conf_state = 0;
+ l2cap_sock_clear_timer(sk);
+
+ 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_state = BT_CONNECTED;
+ sk->sk_state_change(sk);
+ parent->sk_data_ready(parent, 0);
+ }
+}
+
static void l2cap_conn_ready(struct l2cap_conn *conn)
{
struct l2cap_chan_list *l = &conn->chan_list;
@@ -640,14 +665,10 @@ static void l2cap_conn_ready(struct l2cap_conn *conn)
bh_lock_sock(sk);
if (conn->hcon->type == LE_LINK) {
- l2cap_sock_clear_timer(sk);
- sk->sk_state = BT_CONNECTED;
- sk->sk_state_change(sk);
if (smp_conn_security(conn, l2cap_pi(sk)->sec_level))
- BT_DBG("Insufficient security");
- }
+ l2cap_chan_ready(sk);
- if (sk->sk_type != SOCK_SEQPACKET &&
+ } else if (sk->sk_type != SOCK_SEQPACKET &&
sk->sk_type != SOCK_STREAM) {
l2cap_sock_clear_timer(sk);
sk->sk_state = BT_CONNECTED;
@@ -692,6 +713,7 @@ static void l2cap_info_timeout(unsigned long arg)
static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status)
{
struct l2cap_conn *conn = hcon->l2cap_data;
+ struct hci_dev *hdev = hcon->hdev;
if (conn || status)
return conn;
@@ -705,10 +727,10 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status)
BT_DBG("hcon %p conn %p", hcon, conn);
- if (hcon->hdev->le_mtu && hcon->type == LE_LINK)
- conn->mtu = hcon->hdev->le_mtu;
+ if (hdev->le_mtu && hcon->type == LE_LINK)
+ conn->mtu = hdev->le_mtu;
else
- conn->mtu = hcon->hdev->acl_mtu;
+ conn->mtu = hdev->acl_mtu;
conn->src = &hcon->hdev->bdaddr;
conn->dst = &hcon->dst;
@@ -1199,7 +1221,7 @@ static int l2cap_do_connect(struct sock *sk)
sk->sk_type != SOCK_STREAM) {
l2cap_sock_clear_timer(sk);
sk->sk_state = BT_CONNECTED;
- } else
+ } else if (hcon->type != LE_LINK)
l2cap_do_start(sk);
}
@@ -1467,9 +1489,6 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn)
l2cap_sock_set_timer(sk, sk->sk_sndtimeo);
- sk->sk_state = BT_CONNECTED;
- parent->sk_data_ready(parent, 0);
-
write_unlock_bh(&list->lock);
clean:
@@ -2409,27 +2428,32 @@ static int l2cap_sock_release(struct socket *sock)
return err;
}
-static void l2cap_chan_ready(struct sock *sk)
+void l2cap_le_do_start(struct l2cap_conn *conn)
{
- struct sock *parent = bt_sk(sk)->parent;
+ struct l2cap_chan_list *l = &conn->chan_list;
+ struct sock *sk;
- BT_DBG("sk %p, parent %p", sk, parent);
+ read_lock(&l->lock);
- l2cap_pi(sk)->conf_state = 0;
- l2cap_sock_clear_timer(sk);
+ for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) {
+ bh_lock_sock(sk);
- 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);
+ if (l2cap_pi(sk)->scid == L2CAP_CID_LE_DATA) {
+ struct sock *parent = bt_sk(sk)->parent;
+
+ l2cap_sock_clear_timer(sk);
+
+ if (parent)
+ parent->sk_data_ready(parent, 0);
+
+ sk->sk_state = BT_CONNECTED;
+ sk->sk_state_change(sk);
+ }
+
+ bh_unlock_sock(sk);
}
+
+ read_unlock(&l->lock);
}
/* Copy frame to all raw sockets on that connection */
@@ -4742,6 +4766,10 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
BT_DBG("conn %p", conn);
+ if (hcon->type == LE_LINK)
+ if (encrypt)
+ l2cap_le_do_start(conn);
+
read_lock(&l->lock);
for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) {
--
1.7.3.2
This adds support for starting SMP Phase 2 Encryption, when the initial
SMP negotiation is successful. This adds the LE Start Encryption and LE
Long Term Key Request commands and related events.
Signed-off-by: Vinicius Costa Gomes <[email protected]>
---
include/net/bluetooth/hci.h | 34 +++++++++++++++++++
include/net/bluetooth/hci_core.h | 5 +++
net/bluetooth/hci_conn.c | 47 ++++++++++++++++++++++++++
net/bluetooth/hci_event.c | 67 ++++++++++++++++++++++++++++++++++++++
net/bluetooth/smp.c | 10 +++++-
5 files changed, 162 insertions(+), 1 deletions(-)
diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index 2103731..036fdae 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -626,6 +626,33 @@ struct hci_cp_le_create_conn {
#define HCI_OP_LE_CREATE_CONN_CANCEL 0x200e
+#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
@@ -897,6 +924,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;
+
/* Internal events generated by Bluetooth stack */
#define HCI_EV_STACK_INTERNAL 0xfd
struct hci_ev_stack_internal {
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index ee5efa8..cfbe56c 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -192,6 +192,7 @@ struct hci_conn {
__u8 sec_level;
__u8 power_save;
__u16 disc_timeout;
+ __u8 ltk[16];
unsigned long pend;
unsigned int sent;
@@ -713,4 +714,8 @@ struct hci_sec_filter {
void hci_req_complete(struct hci_dev *hdev, int result);
+void hci_le_start_enc(struct hci_conn *conn, 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 */
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index d9a31b6..674829d 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -182,6 +182,53 @@ void hci_setup_sync(struct hci_conn *conn, __u16 handle)
hci_send_cmd(hdev, HCI_OP_SETUP_SYNC_CONN, sizeof(cp), &cp);
}
+void hci_le_start_enc(struct hci_conn *conn, 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, 16);
+
+ 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);
+}
+EXPORT_SYMBOL(hci_le_ltk_neg_reply);
+
/* Device _must_ be locked */
void hci_sco_setup(struct hci_conn *conn, __u8 status)
{
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index df6eaa1..57560fb 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -559,6 +559,30 @@ static void hci_cc_le_read_buffer_size(struct hci_dev *hdev,
hci_req_complete(hdev, rp->status);
}
+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, 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, rp->status);
+}
+
static inline void hci_cs_inquiry(struct hci_dev *hdev, __u8 status)
{
BT_DBG("%s status 0x%x", hdev->name, status);
@@ -920,6 +944,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);
@@ -1440,6 +1469,14 @@ static inline void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *sk
hci_cc_le_read_buffer_size(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;
@@ -1510,6 +1547,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;
@@ -2013,6 +2054,28 @@ unlock:
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;
@@ -2024,6 +2087,10 @@ static inline void hci_le_meta_evt(struct hci_dev *hdev, struct sk_buff *skb)
hci_le_conn_complete_evt(hdev, skb);
break;
+ case HCI_EV_LE_LTK_REQ:
+ hci_le_ltk_request_evt(hdev, skb);
+ break;
+
default:
break;
}
diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c
index a8d2e12..6cfd088 100644
--- a/net/bluetooth/smp.c
+++ b/net/bluetooth/smp.c
@@ -287,7 +287,8 @@ static void smp_cmd_pairing_confirm(struct l2cap_conn *conn, struct sk_buff *skb
static void smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb)
{
- struct crypto_blkcipher *tfm = conn->hcon->hdev->tfm;
+ struct hci_conn *hcon = conn->hcon;
+ struct crypto_blkcipher *tfm = hcon->hdev->tfm;
int ret;
u8 k[16], key[16], res[16], random[16], confirm[16], buf[128];
@@ -295,6 +296,7 @@ static void smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb)
skb_pull(skb, 16);
memset(k, 0, sizeof(k));
+ memset(hcon->ltk, 0, sizeof(hcon->ltk));
if (conn->hcon->out)
ret = smp_c1(tfm, k, random, conn->preq, conn->pres, 0,
@@ -319,6 +321,10 @@ static void smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb)
if (conn->hcon->out) {
smp_s1(tfm, k, random, conn->prnd, key);
+ swap128(key, hcon->ltk);
+
+ hci_le_start_enc(conn->hcon, hcon->ltk);
+
hex_dump_to_buffer(key, sizeof(key), 16, 1, buf, sizeof(buf), 0);
BT_DBG("key %s", buf);
@@ -331,6 +337,8 @@ static void smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb)
smp_s1(tfm, k, conn->prnd, random, key);
+ swap128(key, hcon->ltk);
+
hex_dump_to_buffer(key, sizeof(key), 16, 1, buf, sizeof(buf), 0);
BT_DBG("key %s", buf);
--
1.7.3.2
This adds supports for verifying the confirmation value that the
remote side has sent. This includes support for generating and sending
the random value used to produce the confirmation value.
Signed-off-by: Vinicius Costa Gomes <[email protected]>
---
include/net/bluetooth/l2cap.h | 5 +
net/bluetooth/smp.c | 240 +++++++++++++++++++++++++++--------------
2 files changed, 164 insertions(+), 81 deletions(-)
diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
index cba4423..1d0555f 100644
--- a/include/net/bluetooth/l2cap.h
+++ b/include/net/bluetooth/l2cap.h
@@ -290,6 +290,11 @@ struct l2cap_conn {
__u8 disc_reason;
+ __u8 preq[7];
+ __u8 pres[7];
+ __u8 prnd[16];
+ __u8 pcnf[16];
+
struct l2cap_chan_list chan_list;
};
diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c
index 8e8be47..a8d2e12 100644
--- a/net/bluetooth/smp.c
+++ b/net/bluetooth/smp.c
@@ -27,21 +27,31 @@
#include <linux/crypto.h>
#include <crypto/b128ops.h>
-/* Criptographic toolbox functions */
+static inline void swap128(u8 src[16], u8 dst[16])
+{
+ int i;
+ for (i = 0; i < 16; i++)
+ dst[15 - i] = src[i];
+}
-static int smp_e(const u8 *k, const u8 *r)
+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 crypto_blkcipher *tfm;
struct blkcipher_desc desc;
- struct scatterlist sg[1];
+ struct scatterlist sg;
int err, iv_len;
unsigned char iv[128];
- tfm = crypto_alloc_blkcipher("ecb(aes)", 0, CRYPTO_ALG_ASYNC);
- if (IS_ERR(tfm)) {
- BT_ERR("Failed to load transform for ecb(aes): %ld",
- PTR_ERR(tfm));
- return PTR_ERR(tfm);
+ if (tfm == NULL) {
+ BT_ERR("tfm %p", tfm);
+ err = -1;
+ goto out;
}
desc.tfm = tfm;
@@ -49,12 +59,11 @@ static int smp_e(const u8 *k, const u8 *r)
err = crypto_blkcipher_setkey(tfm, k, 16);
if (err) {
- BT_ERR("smp_e: cipher setkey failed: %d", err);
+ BT_ERR("cipher setkey failed: %d", err);
goto out;
}
- sg_init_table(sg, 1);
- sg_set_buf(sg, r, 16);
+ sg_init_one(&sg, r, 16);
iv_len = crypto_blkcipher_ivsize(tfm);
if (iv_len) {
@@ -62,73 +71,57 @@ static int smp_e(const u8 *k, const u8 *r)
crypto_blkcipher_set_iv(tfm, iv, iv_len);
}
- err = crypto_blkcipher_encrypt(&desc, sg, sg, 16);
+ err = crypto_blkcipher_encrypt(&desc, &sg, &sg, 16);
if (err)
- BT_ERR("smp_e: Encrypt data error %d", err);
+ BT_ERR("Encrypt data error %d", err);
out:
- crypto_free_blkcipher(tfm);
return err;
}
-static int smp_ah(const u8 *k, const u8 *r, u8 *res)
+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 _r[16];
- int err;
-
- /* _r = padding || r */
- memset(_r, 0, 16);
- memcpy(_r + 13, r, 3);
-
- err = smp_e(k, _r);
- if (err) {
- BT_ERR("smp_ah: Encrypt data error");
- goto out;
- }
-
- /* Returns last 24 bits from previous intermediate result _r */
- memcpy(res, _r + 13, 3);
-
-out:
- return err;
-}
-
-static int smp_c1(const u8 *k, const u8 *r, const u8 *pres, const u8 *preq,
- const u8 _iat, const u8 *ia, const u8 _rat, const u8 *ra,
- u8 *res)
-{
- u8 p1[16], p2[16];
+ u8 p1[16], p2[16], pair[7];
+ bdaddr_t addr;
int err;
/* p1 = pres || preq || _rat || _iat */
memset(p1, 0, 16);
- memcpy(p1, pres, 7);
- memcpy(p1 + 7, preq, 7);
+ swap56(pres, pair);
+
+ memcpy(p1, pair, 7);
+ swap56(preq, pair);
+
+ memcpy(p1 + 7, pair, 7);
*(p1 + 14) = _rat;
*(p1 + 15) = _iat;
/* p2 = padding || ia || ra */
memset(p2, 0, 16);
- memcpy(p2 + 4, ia, 6);
- memcpy(p2 + 10, ra, 6);
+ baswap(&addr, ia);
+ memcpy(p2 + 4, &addr, 6);
+ baswap(&addr, ra);
+ memcpy(p2 + 10, &addr, 6);
/* res = r XOR p1 */
- u128_xor((u128 *)res, (u128 *)r, (u128 *)p1);
+ u128_xor((u128 *) res, (u128 *) r, (u128 *) p1);
/* res = e(k, res) */
- err = smp_e(k, res);
+ err = smp_e(tfm, k, res);
if (err) {
- BT_ERR("smp_c1: Encrypt data error");
+ BT_ERR("Encrypt data error");
goto out;
}
/* res = res XOR p2 */
- u128_xor((u128 *)res, (u128 *)res, (u128 *)p2);
+ u128_xor((u128 *) res, (u128 *) res, (u128 *) p2);
/* res = e(k, res) */
- err = smp_e(k, res);
+ err = smp_e(tfm, k, res);
if (err) {
- BT_ERR("smp_c1: Encrypt data error");
+ BT_ERR("Encrypt data error");
goto out;
}
@@ -136,15 +129,19 @@ out:
return err;
}
-static int smp_s1(const u_char *k, const u_char *r1, const u_char *r2, u_char *_r)
+static int smp_s1(struct crypto_blkcipher *tfm, u8 k[16],
+ u8 r1[16], u8 r2[16], u8 _r[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);
+ swap128(r1, r);
+ memcpy(_r, r + 8, 8);
+ swap128(r2, r);
+ memcpy(_r + 8, r + 8, 8);
- err = smp_e(k, _r);
+ err = smp_e(tfm, k, _r);
if (err) {
BT_ERR("smp_s1: Encrypt data error");
goto out;
@@ -154,6 +151,13 @@ out:
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)
{
@@ -199,7 +203,9 @@ static void smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
BT_DBG("");
- skb_pull(skb, sizeof(struct smp_cmd_pairing));
+ conn->preq[0] = SMP_CMD_PAIRING_REQ;
+ memcpy(&conn->preq[1], rp, sizeof(*rp));
+ skb_pull(skb, sizeof(*rp));
rp->io_capability = 0x00;
rp->oob_flag = 0x00;
@@ -208,64 +214,126 @@ static void smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
rp->resp_key_dist = 0x00;
rp->auth_req &= 0x05;
+ conn->pres[0] = SMP_CMD_PAIRING_RSP;
+ memcpy(&conn->pres[1], rp, sizeof(rp));
+
smp_send_cmd(conn, SMP_CMD_PAIRING_RSP, sizeof(*rp), rp);
}
static void smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb)
{
+ struct smp_cmd_pairing *rp = (void *) skb->data;
struct smp_cmd_pairing_confirm cp;
+ struct crypto_blkcipher *tfm = conn->hcon->hdev->tfm;
+ int ret;
+ u8 k[16], res[16];
- BT_DBG("");
+ /* Just Works */
+ memset(k, 0, sizeof(k));
+
+ conn->pres[0] = SMP_CMD_PAIRING_RSP;
+ memcpy(&conn->pres[1], rp, sizeof(*rp));
+ skb_pull(skb, sizeof(*rp));
- memset(&cp, 0, sizeof(struct smp_cmd_pairing_confirm));
+ ret = smp_rand(conn->prnd);
+ if (ret)
+ return;
+
+ ret = smp_c1(tfm, k, conn->prnd, conn->preq, conn->pres, 0,
+ conn->src, 0, conn->dst, res);
+ if (ret)
+ return;
smp_send_cmd(conn, SMP_CMD_PAIRING_CONFIRM, sizeof(cp), &cp);
}
static void smp_cmd_pairing_confirm(struct l2cap_conn *conn, struct sk_buff *skb)
{
+ struct crypto_blkcipher *tfm = conn->hcon->hdev->tfm;
+
BT_DBG("");
- if (conn->hcon->out) {
- struct smp_cmd_pairing_random random;
+ memcpy(conn->pcnf, skb->data, 16);
+ skb_pull(skb, 16);
- BT_DBG("master");
+ if (conn->hcon->out) {
+ u8 random[16];
- memset(&random, 0, sizeof(struct smp_cmd_pairing_random));
+ swap128(conn->prnd, random);
- smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(random),
- &random);
+ smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, 16, random);
} else {
- struct smp_cmd_pairing_confirm confirm;
+ struct smp_cmd_pairing_confirm cp;
+ int ret;
+ u8 k[16], res[16];
+
+ /* Just Works */
+ memset(k, 0, sizeof(k));
- BT_DBG("slave");
+ ret = smp_rand(conn->prnd);
+ if (ret)
+ return;
- memset(&confirm, 0, sizeof(struct smp_cmd_pairing_confirm));
+ ret = smp_c1(tfm, k, conn->prnd, conn->preq, conn->pres, 0,
+ conn->dst, 0, conn->src, res);
+ if (ret)
+ return;
- smp_send_cmd(conn, SMP_CMD_PAIRING_CONFIRM, sizeof(confirm),
- &confirm);
+ swap128(res, cp.confirm_val);
+
+ smp_send_cmd(conn, SMP_CMD_PAIRING_CONFIRM, sizeof(cp), &cp);
}
}
static void smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb)
{
- struct smp_cmd_pairing_random cp;
+ struct crypto_blkcipher *tfm = conn->hcon->hdev->tfm;
+ int ret;
+ u8 k[16], key[16], res[16], random[16], confirm[16], buf[128];
+
+ swap128(skb->data, random);
+ skb_pull(skb, 16);
+
+ memset(k, 0, sizeof(k));
+
+ if (conn->hcon->out)
+ ret = smp_c1(tfm, k, random, conn->preq, conn->pres, 0,
+ conn->src, 0, conn->dst, res);
+ else
+ ret = smp_c1(tfm, k, random, conn->preq, conn->pres, 0,
+ conn->dst, 0, conn->src, res);
+ if (ret)
+ return;
- BT_DBG("");
+ swap128(res, confirm);
- skb_pull(skb, sizeof(struct smp_cmd_pairing_random));
+ if (memcmp(conn->pcnf, confirm, 16) != 0) {
+ struct smp_cmd_pairing_fail cp;
- /* FIXME: check if random matches */
+ BT_ERR("Pairing failed (confirmation values mismatch)");
+ cp.reason = SMP_CONFIRM_FAILED;
+ smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, sizeof(cp), &cp);
+ return;
+ }
if (conn->hcon->out) {
- BT_DBG("master");
- /* FIXME: start encryption */
+ smp_s1(tfm, k, random, conn->prnd, key);
+
+ hex_dump_to_buffer(key, sizeof(key), 16, 1, buf, sizeof(buf), 0);
+
+ BT_DBG("key %s", buf);
} else {
- BT_DBG("slave");
+ u8 r[16];
+
+ swap128(conn->prnd, r);
+
+ smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, 16, r);
+
+ smp_s1(tfm, k, conn->prnd, random, key);
- memset(&cp, 0, sizeof(struct smp_cmd_pairing_random));
+ hex_dump_to_buffer(key, sizeof(key), 16, 1, buf, sizeof(buf), 0);
- smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(cp), &cp);
+ BT_DBG("key %s", buf);
}
}
@@ -276,8 +344,9 @@ static void smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb)
BT_DBG("");
- skb_pull(skb, sizeof(struct smp_cmd_security_req));
- memset(&cp, 0, sizeof(struct smp_cmd_pairing));
+ skb_pull(skb, sizeof(*rp));
+
+ memset(&cp, 0, sizeof(cp));
cp.io_capability = 0x00;
cp.oob_flag = 0x00;
@@ -286,11 +355,15 @@ static void smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb)
cp.resp_key_dist = 0x00;
cp.auth_req = rp->auth_req & 0x05;
+ 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);
}
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, conn->hcon, sec_level);
@@ -311,14 +384,19 @@ int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level)
return 1;
}
- if (conn->hcon->out) {
+ if (hcon->out) {
struct smp_cmd_pairing cp;
+
cp.io_capability = 0x00;
cp.oob_flag = 0x00;
cp.max_key_size = 16;
cp.init_key_dist = 0x00;
cp.resp_key_dist = 0x00;
cp.auth_req = authreq;
+
+ 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);
} else {
struct smp_cmd_security_req cp;
--
1.7.3.2
This will allow using the crypto subsystem for encrypting data. As SMP
(Security Manager Protocol) is implemented almost entirely on the host
side and the crypto module already implements the needed methods
(AES-128), it makes sense to use it.
Signed-off-by: Vinicius Costa Gomes <[email protected]>
---
include/net/bluetooth/hci_core.h | 2 ++
net/bluetooth/hci_core.c | 10 ++++++++++
net/bluetooth/smp.c | 1 -
3 files changed, 12 insertions(+), 1 deletions(-)
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index e2d857a..ee5efa8 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -135,6 +135,8 @@ struct hci_dev {
__u32 req_status;
__u32 req_result;
+ struct crypto_blkcipher *tfm;
+
struct inquiry_cache inq_cache;
struct hci_conn_hash conn_hash;
struct list_head blacklist;
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index 5750b80..0e98ffb 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -41,6 +41,7 @@
#include <linux/interrupt.h>
#include <linux/notifier.h>
#include <linux/rfkill.h>
+#include <linux/crypto.h>
#include <net/sock.h>
#include <asm/system.h>
@@ -947,6 +948,13 @@ int hci_register_dev(struct hci_dev *hdev)
if (!hdev->workqueue)
goto nomem;
+ hdev->tfm = crypto_alloc_blkcipher("ecb(aes)", 0, CRYPTO_ALG_ASYNC);
+ if (IS_ERR(hdev->tfm)) {
+ BT_ERR("Failed to load transform for ecb(aes): %ld",
+ PTR_ERR(hdev->tfm));
+ goto nomem;
+ }
+
hci_register_sysfs(hdev);
hdev->rfkill = rfkill_alloc(hdev->name, &hdev->dev,
@@ -987,6 +995,8 @@ int hci_unregister_dev(struct hci_dev *hdev)
for (i = 0; i < NUM_REASSEMBLY; i++)
kfree_skb(hdev->reassembly[i]);
+ crypto_free_blkcipher(hdev->tfm);
+
hci_notify(hdev, HCI_DEV_UNREG);
if (hdev->rfkill) {
diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c
index 7997978..8e8be47 100644
--- a/net/bluetooth/smp.c
+++ b/net/bluetooth/smp.c
@@ -227,7 +227,6 @@ static void smp_cmd_pairing_confirm(struct l2cap_conn *conn, struct sk_buff *skb
BT_DBG("");
if (conn->hcon->out) {
-
struct smp_cmd_pairing_random random;
BT_DBG("master");
--
1.7.3.2
From: Anderson Briglia <[email protected]>
This patch implements SMP crypto functions called ah, c1, s1 and e.
These functions are needed for SMP keys generation.
Signed-off-by: Anderson Briglia <[email protected]>
Signed-off-by: Anderson Lizardo <[email protected]>
Signed-off-by: Bruna Moreira <[email protected]>
---
net/bluetooth/smp.c | 129 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 129 insertions(+), 0 deletions(-)
diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c
index d3f9828..7997978 100644
--- a/net/bluetooth/smp.c
+++ b/net/bluetooth/smp.c
@@ -24,6 +24,135 @@
#include <net/bluetooth/hci_core.h>
#include <net/bluetooth/l2cap.h>
#include <net/bluetooth/smp.h>
+#include <linux/crypto.h>
+#include <crypto/b128ops.h>
+
+/* Criptographic toolbox functions */
+
+static int smp_e(const u8 *k, const u8 *r)
+{
+ struct crypto_blkcipher *tfm;
+ struct blkcipher_desc desc;
+ struct scatterlist sg[1];
+ int err, iv_len;
+ unsigned char iv[128];
+
+ tfm = crypto_alloc_blkcipher("ecb(aes)", 0, CRYPTO_ALG_ASYNC);
+ if (IS_ERR(tfm)) {
+ BT_ERR("Failed to load transform for ecb(aes): %ld",
+ PTR_ERR(tfm));
+ return PTR_ERR(tfm);
+ }
+
+ desc.tfm = tfm;
+ desc.flags = 0;
+
+ err = crypto_blkcipher_setkey(tfm, k, 16);
+ if (err) {
+ BT_ERR("smp_e: cipher setkey failed: %d", err);
+ goto out;
+ }
+
+ sg_init_table(sg, 1);
+ sg_set_buf(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("smp_e: Encrypt data error %d", err);
+
+out:
+ crypto_free_blkcipher(tfm);
+ return err;
+}
+
+static int smp_ah(const u8 *k, const u8 *r, u8 *res)
+{
+ u8 _r[16];
+ int err;
+
+ /* _r = padding || r */
+ memset(_r, 0, 16);
+ memcpy(_r + 13, r, 3);
+
+ err = smp_e(k, _r);
+ if (err) {
+ BT_ERR("smp_ah: Encrypt data error");
+ goto out;
+ }
+
+ /* Returns last 24 bits from previous intermediate result _r */
+ memcpy(res, _r + 13, 3);
+
+out:
+ return err;
+}
+
+static int smp_c1(const u8 *k, const u8 *r, const u8 *pres, const u8 *preq,
+ const u8 _iat, const u8 *ia, const u8 _rat, const u8 *ra,
+ u8 *res)
+{
+ u8 p1[16], p2[16];
+ int err;
+
+ /* p1 = pres || preq || _rat || _iat */
+ memset(p1, 0, 16);
+ memcpy(p1, pres, 7);
+ memcpy(p1 + 7, preq, 7);
+ *(p1 + 14) = _rat;
+ *(p1 + 15) = _iat;
+
+ /* p2 = padding || ia || ra */
+ memset(p2, 0, 16);
+ memcpy(p2 + 4, ia, 6);
+ memcpy(p2 + 10, ra, 6);
+
+ /* res = r XOR p1 */
+ u128_xor((u128 *)res, (u128 *)r, (u128 *)p1);
+
+ /* res = e(k, res) */
+ err = smp_e(k, res);
+ if (err) {
+ BT_ERR("smp_c1: Encrypt data error");
+ goto out;
+ }
+
+ /* res = res XOR p2 */
+ u128_xor((u128 *)res, (u128 *)res, (u128 *)p2);
+
+ /* res = e(k, res) */
+ err = smp_e(k, res);
+ if (err) {
+ BT_ERR("smp_c1: Encrypt data error");
+ goto out;
+ }
+
+out:
+ return err;
+}
+
+static int smp_s1(const u_char *k, const u_char *r1, const u_char *r2, u_char *_r)
+{
+ 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(k, _r);
+ if (err) {
+ BT_ERR("smp_s1: Encrypt data error");
+ goto out;
+ }
+
+out:
+ return err;
+}
static struct sk_buff *smp_build_cmd(struct l2cap_conn *conn, u8 code,
u16 dlen, void *data)
--
1.7.3.2
From: Anderson Briglia <[email protected]>
This implementation only exchanges SMP messages between the Host and the
Remote. No keys are being generated. TK and STK generation will be
provided in further patches.
Signed-off-by: Vinicius Costa Gomes <[email protected]>
---
net/bluetooth/l2cap_core.c | 3 +-
net/bluetooth/smp.c | 115 ++++++++++++++++++++++++++++++++++++++++++--
2 files changed, 113 insertions(+), 5 deletions(-)
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 8ee2bd5..24719d3 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -4624,7 +4624,8 @@ static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb)
break;
case L2CAP_CID_SMP:
- smp_sig_channel(conn, skb);
+ if (smp_sig_channel(conn, skb))
+ l2cap_conn_del(conn->hcon, 0x05);
break;
default:
diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c
index e9dde5f..d3f9828 100644
--- a/net/bluetooth/smp.c
+++ b/net/bluetooth/smp.c
@@ -64,6 +64,103 @@ static void smp_send_cmd(struct l2cap_conn *conn, u8 code, u16 len, void *data)
hci_send_acl(conn->hcon, skb, 0);
}
+static void smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
+{
+ struct smp_cmd_pairing *rp = (void *) skb->data;
+
+ BT_DBG("");
+
+ skb_pull(skb, sizeof(struct smp_cmd_pairing));
+
+ rp->io_capability = 0x00;
+ rp->oob_flag = 0x00;
+ rp->max_key_size = 16;
+ rp->init_key_dist = 0x00;
+ rp->resp_key_dist = 0x00;
+ rp->auth_req &= 0x05;
+
+ smp_send_cmd(conn, SMP_CMD_PAIRING_RSP, sizeof(*rp), rp);
+}
+
+static void smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb)
+{
+ struct smp_cmd_pairing_confirm cp;
+
+ BT_DBG("");
+
+ memset(&cp, 0, sizeof(struct smp_cmd_pairing_confirm));
+
+ smp_send_cmd(conn, SMP_CMD_PAIRING_CONFIRM, sizeof(cp), &cp);
+}
+
+static void smp_cmd_pairing_confirm(struct l2cap_conn *conn, struct sk_buff *skb)
+{
+ BT_DBG("");
+
+ if (conn->hcon->out) {
+
+ struct smp_cmd_pairing_random random;
+
+ BT_DBG("master");
+
+ memset(&random, 0, sizeof(struct smp_cmd_pairing_random));
+
+ smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(random),
+ &random);
+ } else {
+ struct smp_cmd_pairing_confirm confirm;
+
+ BT_DBG("slave");
+
+ memset(&confirm, 0, sizeof(struct smp_cmd_pairing_confirm));
+
+ smp_send_cmd(conn, SMP_CMD_PAIRING_CONFIRM, sizeof(confirm),
+ &confirm);
+ }
+}
+
+static void smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb)
+{
+ struct smp_cmd_pairing_random cp;
+
+ BT_DBG("");
+
+ skb_pull(skb, sizeof(struct smp_cmd_pairing_random));
+
+ /* FIXME: check if random matches */
+
+ if (conn->hcon->out) {
+ BT_DBG("master");
+ /* FIXME: start encryption */
+ } else {
+ BT_DBG("slave");
+
+ memset(&cp, 0, sizeof(struct smp_cmd_pairing_random));
+
+ smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(cp), &cp);
+ }
+}
+
+static void 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;
+
+ BT_DBG("");
+
+ skb_pull(skb, sizeof(struct smp_cmd_security_req));
+ memset(&cp, 0, sizeof(struct smp_cmd_pairing));
+
+ cp.io_capability = 0x00;
+ cp.oob_flag = 0x00;
+ cp.max_key_size = 16;
+ cp.init_key_dist = 0x00;
+ cp.resp_key_dist = 0x00;
+ cp.auth_req = rp->auth_req & 0x05;
+
+ smp_send_cmd(conn, SMP_CMD_PAIRING_REQ, sizeof(cp), &cp);
+}
+
int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level)
{
__u8 authreq;
@@ -114,23 +211,33 @@ int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb)
switch (code) {
case SMP_CMD_PAIRING_REQ:
- reason = SMP_PAIRING_NOTSUPP;
- smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, 1, &reason);
- err = -1;
+ smp_cmd_pairing_req(conn, skb);
break;
case SMP_CMD_PAIRING_FAIL:
break;
case SMP_CMD_PAIRING_RSP:
+ smp_cmd_pairing_rsp(conn, skb);
+ break;
+
+ case SMP_CMD_SECURITY_REQ:
+ smp_cmd_security_req(conn, skb);
+ break;
+
case SMP_CMD_PAIRING_CONFIRM:
+ smp_cmd_pairing_confirm(conn, skb);
+ break;
+
case SMP_CMD_PAIRING_RANDOM:
+ 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:
- case SMP_CMD_SECURITY_REQ:
default:
BT_DBG("Unknown command code 0x%2.2x", code);
--
1.7.3.2
From: Anderson Briglia <[email protected]>
Start SMP procedure for LE connections. This modification intercepts l2cap
received frames and call proper SMP functions to start the SMP procedure. By
now, no keys are being used.
Signed-off-by: Vinicius Costa Gomes <[email protected]>
Signed-off-by: Anderson Briglia <[email protected]>
---
net/bluetooth/l2cap_core.c | 7 +++++++
net/bluetooth/smp.c | 2 +-
2 files changed, 8 insertions(+), 1 deletions(-)
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 0d168aa..8ee2bd5 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -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>
#define VERSION "2.15"
@@ -642,6 +643,8 @@ static void l2cap_conn_ready(struct l2cap_conn *conn)
l2cap_sock_clear_timer(sk);
sk->sk_state = BT_CONNECTED;
sk->sk_state_change(sk);
+ if (smp_conn_security(conn, l2cap_pi(sk)->sec_level))
+ BT_DBG("Insufficient security");
}
if (sk->sk_type != SOCK_SEQPACKET &&
@@ -4620,6 +4623,10 @@ static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb)
l2cap_conless_channel(conn, psm, skb);
break;
+ case L2CAP_CID_SMP:
+ smp_sig_channel(conn, skb);
+ break;
+
default:
l2cap_data_channel(conn, cid, skb);
break;
diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c
index e427d11..e9dde5f 100644
--- a/net/bluetooth/smp.c
+++ b/net/bluetooth/smp.c
@@ -86,7 +86,7 @@ int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level)
return 1;
}
- if (conn->hcon->link_mode & HCI_LM_MASTER) {
+ if (conn->hcon->out) {
struct smp_cmd_pairing cp;
cp.io_capability = 0x00;
cp.oob_flag = 0x00;
--
1.7.3.2
These simple commands will allow the SMP procedure to be started
and terminated with a not supported error. This is the first step
toward something useful.
Signed-off-by: Vinicius Costa Gomes <[email protected]>
Signed-off-by: Anderson Briglia <[email protected]>
---
include/net/bluetooth/smp.h | 4 +
net/bluetooth/Makefile | 1 +
net/bluetooth/{l2cap.c => l2cap_core.c} | 0
net/bluetooth/smp.c | 144 +++++++++++++++++++++++++++++++
4 files changed, 149 insertions(+), 0 deletions(-)
rename net/bluetooth/{l2cap.c => l2cap_core.c} (100%)
create mode 100644 net/bluetooth/smp.c
diff --git a/include/net/bluetooth/smp.h b/include/net/bluetooth/smp.h
index 8f2edbf..b9603cc 100644
--- a/include/net/bluetooth/smp.h
+++ b/include/net/bluetooth/smp.h
@@ -73,4 +73,8 @@ struct smp_cmd_security_req {
#define SMP_UNSPECIFIED 0x08
#define SMP_REPEATED_ATTEMPTS 0x09
+/* 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 */
diff --git a/net/bluetooth/Makefile b/net/bluetooth/Makefile
index d1e433f..d138b23 100644
--- a/net/bluetooth/Makefile
+++ b/net/bluetooth/Makefile
@@ -11,3 +11,4 @@ obj-$(CONFIG_BT_CMTP) += cmtp/
obj-$(CONFIG_BT_HIDP) += hidp/
bluetooth-objs := af_bluetooth.o hci_core.o hci_conn.o hci_event.o hci_sock.o hci_sysfs.o lib.o
+l2cap-objs := l2cap_core.o smp.o
diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap_core.c
similarity index 100%
rename from net/bluetooth/l2cap.c
rename to net/bluetooth/l2cap_core.c
diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c
new file mode 100644
index 0000000..e427d11
--- /dev/null
+++ b/net/bluetooth/smp.c
@@ -0,0 +1,144 @@
+/*
+ BlueZ - Bluetooth protocol stack for Linux
+ Copyright (C) 2010 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>
+
+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 + 1 + 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(1 + dlen);
+ lh->cid = cpu_to_le16(L2CAP_CID_SMP);
+
+ memcpy(skb_put(skb, 1), &code, 1);
+
+ 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);
+}
+
+int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level)
+{
+ __u8 authreq;
+
+ BT_DBG("conn %p hcon %p level 0x%2.2x", conn, conn->hcon, sec_level);
+
+ switch (sec_level) {
+ case BT_SECURITY_MEDIUM:
+ /* Encrypted, no MITM protection */
+ authreq = 0x01;
+ break;
+
+ case BT_SECURITY_HIGH:
+ /* Bonding, MITM protection */
+ authreq = 0x05;
+ break;
+
+ case BT_SECURITY_LOW:
+ default:
+ return 1;
+ }
+
+ if (conn->hcon->link_mode & HCI_LM_MASTER) {
+ struct smp_cmd_pairing cp;
+ cp.io_capability = 0x00;
+ cp.oob_flag = 0x00;
+ cp.max_key_size = 16;
+ cp.init_key_dist = 0x00;
+ cp.resp_key_dist = 0x00;
+ cp.auth_req = authreq;
+ 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);
+ }
+
+ return 0;
+}
+
+int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb)
+{
+ __u8 code = skb->data[0];
+ __u8 reason;
+ int err = 0;
+
+ skb_pull(skb, 1);
+
+ switch (code) {
+ case SMP_CMD_PAIRING_REQ:
+ reason = SMP_PAIRING_NOTSUPP;
+ smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, 1, &reason);
+ err = -1;
+ break;
+
+ case SMP_CMD_PAIRING_FAIL:
+ break;
+
+ case SMP_CMD_PAIRING_RSP:
+ case SMP_CMD_PAIRING_CONFIRM:
+ case SMP_CMD_PAIRING_RANDOM:
+ 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:
+ case SMP_CMD_SECURITY_REQ:
+ default:
+ BT_DBG("Unknown command code 0x%2.2x", code);
+
+ reason = SMP_CMD_NOTSUPP;
+ smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, 1, &reason);
+ err = -1;
+ }
+
+ kfree_skb(skb);
+ return err;
+}
--
1.7.3.2
As L2CAP packets coming over LE don't have any more encapsulation,
other than L2CAP, we are able to process them as soon as they arrive.
Signed-off-by: Vinicius Costa Gomes <[email protected]>
---
net/bluetooth/l2cap.c | 17 +++++++++++++++--
1 files changed, 15 insertions(+), 2 deletions(-)
diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c
index e481d6b..0d168aa 100644
--- a/net/bluetooth/l2cap.c
+++ b/net/bluetooth/l2cap.c
@@ -4798,17 +4798,30 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
static int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags)
{
struct l2cap_conn *conn = hcon->l2cap_data;
+ struct l2cap_hdr *hdr;
+ int len;
if (!conn && !(conn = l2cap_conn_add(hcon, 0)))
goto drop;
BT_DBG("conn %p len %d flags 0x%x", conn, skb->len, flags);
+ if (hcon->type == LE_LINK) {
+ hdr = (struct l2cap_hdr *) skb->data;
+ len = __le16_to_cpu(hdr->len) + L2CAP_HDR_SIZE;
+
+ if (len == skb->len) {
+ /* Complete frame received */
+ l2cap_recv_frame(conn, skb);
+ return 0;
+ }
+
+ goto drop;
+ }
+
if (flags & ACL_START) {
- struct l2cap_hdr *hdr;
struct sock *sk;
u16 cid;
- int len;
if (conn->rx_len) {
BT_ERR("Unexpected start frame (len %d)", skb->len);
--
1.7.3.2
From: Ville Tervo <[email protected]>
Add command structures for security manager protocol.
Signed-off-by: Ville Tervo <[email protected]>
---
include/net/bluetooth/smp.h | 76 +++++++++++++++++++++++++++++++++++++++++++
1 files changed, 76 insertions(+), 0 deletions(-)
create mode 100644 include/net/bluetooth/smp.h
diff --git a/include/net/bluetooth/smp.h b/include/net/bluetooth/smp.h
new file mode 100644
index 0000000..8f2edbf
--- /dev/null
+++ b/include/net/bluetooth/smp.h
@@ -0,0 +1,76 @@
+#ifndef __SMP_H
+#define __SMP_H
+
+struct smp_command_hdr {
+ __u8 code;
+} __packed;
+
+#define SMP_CMD_PAIRING_REQ 0x01
+#define SMP_CMD_PAIRING_RSP 0x02
+struct smp_cmd_pairing {
+ __u8 io_capability;
+ __u8 oob_flag;
+ __u8 auth_req;
+ __u8 max_key_size;
+ __u8 init_key_dist;
+ __u8 resp_key_dist;
+} __packed;
+
+#define SMP_CMD_PAIRING_CONFIRM 0x03
+struct smp_cmd_pairing_confirm {
+ __u8 confirm_val[16];
+} __packed;
+
+#define SMP_CMD_PAIRING_RANDOM 0x04
+struct smp_cmd_pairing_random {
+ __u8 rand_val[16];
+} __packed;
+
+#define SMP_CMD_PAIRING_FAIL 0x05
+struct smp_cmd_pairing_fail {
+ __u8 reason;
+} __packed;
+
+#define SMP_CMD_ENCRYPT_INFO 0x06
+struct smp_cmd_encrypt_info {
+ __u8 ltk[16];
+} __packed;
+
+#define SMP_CMD_MASTER_IDENT 0x07
+struct smp_cmd_master_ident {
+ __u16 ediv;
+ __u8 rand[8];
+} __packed;
+
+#define SMP_CMD_IDENT_INFO 0x08
+struct smp_cmd_ident_info {
+ __u8 irk[16];
+} __packed;
+
+#define SMP_CMD_IDENT_ADDR_INFO 0x09
+struct smp_cmd_ident_addr_info {
+ __u8 addr_type;
+ bdaddr_t bdaddr;
+} __packed;
+
+#define SMP_CMD_SIGN_INFO 0x0a
+struct smp_cmd_sign_info {
+ __u8 csrk[16];
+} __packed;
+
+#define SMP_CMD_SECURITY_REQ 0x0b
+struct smp_cmd_security_req {
+ __u8 auth_req;
+} __packed;
+
+#define SMP_PASSKEY_ENTRY_FAILED 0x01
+#define SMP_OOB_NOT_AVAIL 0x02
+#define SMP_AUTH_REQUIREMENTS 0x03
+#define SMP_CONFIRM_FAILED 0x04
+#define SMP_PAIRING_NOTSUPP 0x05
+#define SMP_ENC_KEY_SIZE 0x06
+#define SMP_CMD_NOTSUPP 0x07
+#define SMP_UNSPECIFIED 0x08
+#define SMP_REPEATED_ATTEMPTS 0x09
+
+#endif /* __SMP_H */
--
1.7.3.2
From: Ville Tervo <[email protected]>
Add support for LE server sockets.
Signed-off-by: Ville Tervo <[email protected]>
---
net/bluetooth/l2cap.c | 3 +--
1 files changed, 1 insertions(+), 2 deletions(-)
diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c
index 172bf93..e481d6b 100644
--- a/net/bluetooth/l2cap.c
+++ b/net/bluetooth/l2cap.c
@@ -1438,8 +1438,7 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn)
BT_DBG("");
/* Check if we have socket listening on cid */
- parent = l2cap_get_sock_by_cid(BT_LISTEN, L2CAP_CID_LE_DATA,
- conn->src);
+ parent = l2cap_get_sock_by_cid(BT_LISTEN, L2CAP_CID_LE_DATA, conn->src);
if (!parent)
return;
--
1.7.3.2
From: Ville Tervo <[email protected]>
Don't try to unlock sock if the lock was not held.
Signed-off-by: Ville Tervo <[email protected]>
---
net/bluetooth/l2cap.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c
index 5894662..172bf93 100644
--- a/net/bluetooth/l2cap.c
+++ b/net/bluetooth/l2cap.c
@@ -1441,7 +1441,7 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn)
parent = l2cap_get_sock_by_cid(BT_LISTEN, L2CAP_CID_LE_DATA,
conn->src);
if (!parent)
- goto clean;
+ return;
/* Check for backlog size */
if (sk_acceptq_is_full(parent)) {
--
1.7.3.2
From: Ville Tervo <[email protected]>
Separate LE and ACL timeouts. Othervise ACL connections
on non LE hw will time out after 45 secs.
Signed-off-by: Ville Tervo <[email protected]>
---
net/bluetooth/hci_core.c | 14 +++++++-------
1 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index 45c78c2..5750b80 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -1495,19 +1495,19 @@ static inline struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type, int
return conn;
}
-static inline void hci_acl_tx_to(struct hci_dev *hdev)
+static inline void hci_link_tx_to(struct hci_dev *hdev, __u8 type)
{
struct hci_conn_hash *h = &hdev->conn_hash;
struct list_head *p;
struct hci_conn *c;
- BT_ERR("%s ACL tx timeout", hdev->name);
+ BT_ERR("%s link tx timeout", hdev->name);
/* Kill stalled connections */
list_for_each(p, &h->list) {
c = list_entry(p, struct hci_conn, list);
- if (c->type == ACL_LINK && c->sent) {
- BT_ERR("%s killing stalled ACL connection %s",
+ if (c->type == type && c->sent) {
+ BT_ERR("%s killing stalled connection %s",
hdev->name, batostr(&c->dst));
hci_acl_disconn(c, 0x13);
}
@@ -1526,7 +1526,7 @@ static inline void hci_sched_acl(struct hci_dev *hdev)
/* ACL tx timeout must be longer than maximum
* link supervision timeout (40.9 seconds) */
if (!hdev->acl_cnt && time_after(jiffies, hdev->acl_last_tx + HZ * 45))
- hci_acl_tx_to(hdev);
+ hci_link_tx_to(hdev, ACL_LINK);
}
while (hdev->acl_cnt && (conn = hci_low_sent(hdev, ACL_LINK, "e))) {
@@ -1596,9 +1596,9 @@ static inline void hci_sched_le(struct hci_dev *hdev)
if (!test_bit(HCI_RAW, &hdev->flags)) {
/* ACL tx timeout must be longer than maximum
* link supervision timeout (40.9 seconds) */
- if (!hdev->le_cnt &&
+ if (!hdev->le_cnt && hdev->le_pkts &&
time_after(jiffies, hdev->le_last_tx + HZ * 45))
- hci_acl_tx_to(hdev);
+ hci_link_tx_to(hdev, LE_LINK);
}
cnt = hdev->le_pkts ? hdev->le_cnt : hdev->acl_cnt;
--
1.7.3.2
Fix LE connections not being marked as master.
Signed-off-by: Vinicius Costa Gomes <[email protected]>
---
net/bluetooth/hci_conn.c | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index d230a46..d9a31b6 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -52,6 +52,7 @@ static void hci_le_connect(struct hci_conn *conn)
conn->state = BT_CONNECT;
conn->out = 1;
+ conn->link_mode |= HCI_LM_MASTER;
memset(&cp, 0, sizeof(cp));
cp.scan_interval = cpu_to_le16(0x0004);
--
1.7.3.2
From: Ville Tervo <[email protected]>
l2cap over LE links can be disconnected without sending
disconnect command first.
Signed-off-by: Ville Tervo <[email protected]>
---
net/bluetooth/l2cap.c | 15 ++++++++++-----
1 files changed, 10 insertions(+), 5 deletions(-)
diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c
index 0b583c4..5894662 100644
--- a/net/bluetooth/l2cap.c
+++ b/net/bluetooth/l2cap.c
@@ -873,6 +873,8 @@ static void l2cap_sock_kill(struct sock *sk)
static void __l2cap_sock_close(struct sock *sk, int reason)
{
+ struct l2cap_conn *conn;
+
BT_DBG("sk %p state %d socket %p", sk, sk->sk_state, sk->sk_socket);
switch (sk->sk_state) {
@@ -882,8 +884,10 @@ static void __l2cap_sock_close(struct sock *sk, int reason)
case BT_CONNECTED:
case BT_CONFIG:
- if (sk->sk_type == SOCK_SEQPACKET ||
- sk->sk_type == SOCK_STREAM) {
+ conn = l2cap_pi(sk)->conn;
+ if ((sk->sk_type == SOCK_SEQPACKET ||
+ sk->sk_type == SOCK_STREAM) &&
+ conn->hcon->type != LE_LINK) {
struct l2cap_conn *conn = l2cap_pi(sk)->conn;
l2cap_sock_set_timer(sk, sk->sk_sndtimeo);
@@ -893,9 +897,10 @@ static void __l2cap_sock_close(struct sock *sk, int reason)
break;
case BT_CONNECT2:
- if (sk->sk_type == SOCK_SEQPACKET ||
- sk->sk_type == SOCK_STREAM) {
- struct l2cap_conn *conn = l2cap_pi(sk)->conn;
+ conn = l2cap_pi(sk)->conn;
+ if ((sk->sk_type == SOCK_SEQPACKET ||
+ sk->sk_type == SOCK_STREAM) &&
+ conn->hcon->type != LE_LINK) {
struct l2cap_conn_rsp rsp;
__u16 result;
--
1.7.3.2
From: Ville Tervo <[email protected]>
Add support for LE server sockets.
Signed-off-by: Ville Tervo <[email protected]>
---
include/net/bluetooth/l2cap.h | 1 +
net/bluetooth/hci_event.c | 10 +++-
net/bluetooth/l2cap.c | 102 +++++++++++++++++++++++++++++++++++++++--
3 files changed, 106 insertions(+), 7 deletions(-)
diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
index cc3a140..cba4423 100644
--- a/include/net/bluetooth/l2cap.h
+++ b/include/net/bluetooth/l2cap.h
@@ -38,6 +38,7 @@
#define L2CAP_DEFAULT_MAX_PDU_SIZE 1009 /* Sized for 3-DH5 packet */
#define L2CAP_DEFAULT_ACK_TO 200
#define L2CAP_LOCAL_BUSY_TRIES 12
+#define L2CAP_LE_DEFAULT_MTU 23
#define L2CAP_CONN_TIMEOUT (40000) /* 40 seconds */
#define L2CAP_INFO_TIMEOUT (4000) /* 4 seconds */
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index 6735804..df6eaa1 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -1985,8 +1985,14 @@ static inline void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff
hci_dev_lock(hdev);
conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &ev->bdaddr);
- if (!conn)
- goto unlock;
+ if (!conn) {
+ conn = hci_conn_add(hdev, LE_LINK, &ev->bdaddr);
+ if (!conn) {
+ BT_ERR("No memory for new connection");
+ hci_dev_unlock(hdev);
+ return;
+ }
+ }
if (ev->status) {
hci_proto_connect_cfm(conn, ev->status);
diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c
index b7a5074..0b583c4 100644
--- a/net/bluetooth/l2cap.c
+++ b/net/bluetooth/l2cap.c
@@ -82,6 +82,8 @@ static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn,
static int l2cap_ertm_data_rcv(struct sock *sk, struct sk_buff *skb);
+static void l2cap_le_conn_ready(struct l2cap_conn *conn);
+
/* ---- L2CAP timers ---- */
static void l2cap_sock_set_timer(struct sock *sk, long timeout)
{
@@ -236,8 +238,16 @@ static void __l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct so
l2cap_pi(sk)->conn = conn;
if (sk->sk_type == SOCK_SEQPACKET || sk->sk_type == SOCK_STREAM) {
- /* Alloc CID for connection-oriented socket */
- l2cap_pi(sk)->scid = l2cap_alloc_cid(l);
+ if (conn->hcon->type == LE_LINK) {
+ /* LE connection */
+ l2cap_pi(sk)->omtu = L2CAP_LE_DEFAULT_MTU;
+ l2cap_pi(sk)->scid = L2CAP_CID_LE_DATA;
+ l2cap_pi(sk)->dcid = L2CAP_CID_LE_DATA;
+ } else {
+ /* Alloc CID for connection-oriented socket */
+ l2cap_pi(sk)->scid = l2cap_alloc_cid(l);
+ l2cap_pi(sk)->omtu = L2CAP_DEFAULT_MTU;
+ }
} else if (sk->sk_type == SOCK_DGRAM) {
/* Connectionless socket */
l2cap_pi(sk)->scid = L2CAP_CID_CONN_LESS;
@@ -620,6 +630,9 @@ static void l2cap_conn_ready(struct l2cap_conn *conn)
BT_DBG("conn %p", conn);
+ if (!conn->hcon->out && conn->hcon->type == LE_LINK)
+ l2cap_le_conn_ready(conn);
+
read_lock(&l->lock);
for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) {
@@ -702,7 +715,8 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status)
spin_lock_init(&conn->lock);
rwlock_init(&conn->chan_list.lock);
- setup_timer(&conn->info_timer, l2cap_info_timeout,
+ if (hcon->type != LE_LINK)
+ setup_timer(&conn->info_timer, l2cap_info_timeout,
(unsigned long) conn);
conn->disc_reason = 0x13;
@@ -796,6 +810,37 @@ static void l2cap_sock_destruct(struct sock *sk)
skb_queue_purge(&sk->sk_write_queue);
}
+static inline struct sock *l2cap_get_sock_by_cid(int state, __le16 cid, bdaddr_t *src)
+{
+ struct sock *s;
+ struct sock *sk = NULL, *sk1 = NULL;
+ struct hlist_node *node;
+
+ read_lock(&l2cap_sk_list.lock);
+ sk_for_each(sk, node, &l2cap_sk_list.head) {
+ if (state && sk->sk_state != state)
+ continue;
+
+ if (l2cap_pi(sk)->dcid == cid) {
+ /* Exact match. */
+ if (!bacmp(&bt_sk(sk)->src, src))
+ break;
+
+ /* Closest match */
+ if (!bacmp(&bt_sk(sk)->src, BDADDR_ANY))
+ sk1 = sk;
+ }
+ }
+
+ s = node ? sk : sk1;
+
+ if (s)
+ bh_lock_sock(s);
+ read_unlock(&l2cap_sk_list.lock);
+
+ return s;
+}
+
static void l2cap_sock_cleanup_listen(struct sock *parent)
{
struct sock *sk;
@@ -1008,7 +1053,7 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen)
len = min_t(unsigned int, sizeof(la), alen);
memcpy(&la, addr, len);
- if (la.l2_cid)
+ if (la.l2_cid && la.l2_psm)
return -EINVAL;
lock_sock(sk);
@@ -1050,6 +1095,9 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen)
l2cap_pi(sk)->sec_level = BT_SECURITY_SDP;
}
+ if (la.l2_cid)
+ l2cap_pi(sk)->dcid = la.l2_cid;
+
write_unlock_bh(&l2cap_sk_list.lock);
done:
@@ -1267,7 +1315,7 @@ static int l2cap_sock_listen(struct socket *sock, int backlog)
goto done;
}
- if (!l2cap_pi(sk)->psm) {
+ if (!l2cap_pi(sk)->psm && !l2cap_pi(sk)->dcid) {
bdaddr_t *src = &bt_sk(sk)->src;
u16 psm;
@@ -1377,6 +1425,50 @@ static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr, int *l
return 0;
}
+static void l2cap_le_conn_ready(struct l2cap_conn *conn)
+{
+ struct l2cap_chan_list *list = &conn->chan_list;
+ struct sock *parent, *uninitialized_var(sk);
+
+ BT_DBG("");
+
+ /* Check if we have socket listening on cid */
+ parent = l2cap_get_sock_by_cid(BT_LISTEN, L2CAP_CID_LE_DATA,
+ conn->src);
+ if (!parent)
+ goto clean;
+
+ /* Check for backlog size */
+ if (sk_acceptq_is_full(parent)) {
+ BT_DBG("backlog full %d", parent->sk_ack_backlog);
+ goto clean;
+ }
+
+ sk = l2cap_sock_alloc(sock_net(parent), NULL, BTPROTO_L2CAP, GFP_ATOMIC);
+ if (!sk)
+ goto clean;
+
+ write_lock_bh(&list->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);
+
+ __l2cap_chan_add(conn, sk, parent);
+
+ l2cap_sock_set_timer(sk, sk->sk_sndtimeo);
+
+ sk->sk_state = BT_CONNECTED;
+ parent->sk_data_ready(parent, 0);
+
+ write_unlock_bh(&list->lock);
+
+clean:
+ bh_unlock_sock(parent);
+}
+
static int __l2cap_wait_ack(struct sock *sk)
{
DECLARE_WAITQUEUE(wait, current);
--
1.7.3.2
From: Ville Tervo <[email protected]>
Add basic LE connection support to L2CAP. LE
connection can be created by specifying cid
in struct sockaddr_l2
Signed-off-by: Ville Tervo <[email protected]>
---
include/net/bluetooth/l2cap.h | 3 +++
net/bluetooth/l2cap.c | 32 ++++++++++++++++++++++++--------
2 files changed, 27 insertions(+), 8 deletions(-)
diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
index c819c8b..cc3a140 100644
--- a/include/net/bluetooth/l2cap.h
+++ b/include/net/bluetooth/l2cap.h
@@ -160,6 +160,9 @@ struct l2cap_conn_rsp {
/* channel indentifier */
#define L2CAP_CID_SIGNALING 0x0001
#define L2CAP_CID_CONN_LESS 0x0002
+#define L2CAP_CID_LE_DATA 0x0004
+#define L2CAP_CID_LE_SIGNALING 0x0005
+#define L2CAP_CID_SMP 0x0006
#define L2CAP_CID_DYN_START 0x0040
#define L2CAP_CID_DYN_END 0xffff
diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c
index 12b4aa2..b7a5074 100644
--- a/net/bluetooth/l2cap.c
+++ b/net/bluetooth/l2cap.c
@@ -625,6 +625,12 @@ static void l2cap_conn_ready(struct l2cap_conn *conn)
for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) {
bh_lock_sock(sk);
+ if (conn->hcon->type == LE_LINK) {
+ l2cap_sock_clear_timer(sk);
+ sk->sk_state = BT_CONNECTED;
+ sk->sk_state_change(sk);
+ }
+
if (sk->sk_type != SOCK_SEQPACKET &&
sk->sk_type != SOCK_STREAM) {
l2cap_sock_clear_timer(sk);
@@ -683,7 +689,11 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status)
BT_DBG("hcon %p conn %p", hcon, conn);
- conn->mtu = hcon->hdev->acl_mtu;
+ if (hcon->hdev->le_mtu && hcon->type == LE_LINK)
+ conn->mtu = hcon->hdev->le_mtu;
+ else
+ conn->mtu = hcon->hdev->acl_mtu;
+
conn->src = &hcon->hdev->bdaddr;
conn->dst = &hcon->dst;
@@ -1102,8 +1112,13 @@ static int l2cap_do_connect(struct sock *sk)
}
}
- hcon = hci_connect(hdev, ACL_LINK, dst,
+ if (l2cap_pi(sk)->dcid == L2CAP_CID_LE_DATA)
+ hcon = hci_connect(hdev, LE_LINK, dst,
+ l2cap_pi(sk)->sec_level, auth_type);
+ else
+ hcon = hci_connect(hdev, ACL_LINK, dst,
l2cap_pi(sk)->sec_level, auth_type);
+
if (!hcon)
goto done;
@@ -1154,13 +1169,13 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int al
len = min_t(unsigned int, sizeof(la), alen);
memcpy(&la, addr, len);
- if (la.l2_cid)
+ if (la.l2_cid && la.l2_psm)
return -EINVAL;
lock_sock(sk);
if ((sk->sk_type == SOCK_SEQPACKET || sk->sk_type == SOCK_STREAM)
- && !la.l2_psm) {
+ && !(la.l2_psm || la.l2_cid)) {
err = -EINVAL;
goto done;
}
@@ -1202,14 +1217,15 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int al
/* PSM must be odd and lsb of upper byte must be 0 */
if ((__le16_to_cpu(la.l2_psm) & 0x0101) != 0x0001 &&
- sk->sk_type != SOCK_RAW) {
+ sk->sk_type != SOCK_RAW && !la.l2_cid) {
err = -EINVAL;
goto done;
}
- /* Set destination address and psm */
+ /* Set destination address and psm or cid */
bacpy(&bt_sk(sk)->dst, &la.l2_bdaddr);
l2cap_pi(sk)->psm = la.l2_psm;
+ l2cap_pi(sk)->dcid = la.l2_cid;
err = l2cap_do_connect(sk);
if (err)
@@ -4555,7 +4571,7 @@ static int l2cap_connect_cfm(struct hci_conn *hcon, u8 status)
BT_DBG("hcon %p bdaddr %s status %d", hcon, batostr(&hcon->dst), status);
- if (hcon->type != ACL_LINK)
+ if (!(hcon->type == ACL_LINK || hcon->type == LE_LINK))
return -EINVAL;
if (!status) {
@@ -4584,7 +4600,7 @@ static int l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason)
{
BT_DBG("hcon %p reason %d", hcon, reason);
- if (hcon->type != ACL_LINK)
+ if (!(hcon->type == ACL_LINK || hcon->type == LE_LINK))
return -EINVAL;
l2cap_conn_del(hcon, bt_err(reason));
--
1.7.3.2
From: Ville Tervo <[email protected]>
BLuetooth chips may have separate buffers for
LE traffic. This patch add support to use LE
buffers provided by the chip.
Signed-off-by: Ville Tervo <[email protected]>
---
include/net/bluetooth/hci.h | 1 +
include/net/bluetooth/hci_core.h | 5 +++
net/bluetooth/hci_conn.c | 5 +++
net/bluetooth/hci_core.c | 74 +++++++++++++++++++++++++++++++++++--
net/bluetooth/hci_event.c | 40 +++++++++++++++++++-
5 files changed, 119 insertions(+), 6 deletions(-)
diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index 02055b9..2103731 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -189,6 +189,7 @@ enum {
#define LMP_EV4 0x01
#define LMP_EV5 0x02
+#define LMP_LE 0x40
#define LMP_SNIFF_SUBR 0x02
#define LMP_EDR_ESCO_2M 0x20
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index 2b7f94a..e2d857a 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -103,15 +103,19 @@ struct hci_dev {
atomic_t cmd_cnt;
unsigned int acl_cnt;
unsigned int sco_cnt;
+ unsigned int le_cnt;
unsigned int acl_mtu;
unsigned int sco_mtu;
+ unsigned int le_mtu;
unsigned int acl_pkts;
unsigned int sco_pkts;
+ unsigned int le_pkts;
unsigned long cmd_last_tx;
unsigned long acl_last_tx;
unsigned long sco_last_tx;
+ unsigned long le_last_tx;
struct workqueue_struct *workqueue;
@@ -473,6 +477,7 @@ void hci_conn_del_sysfs(struct hci_conn *conn);
#define lmp_sniffsubr_capable(dev) ((dev)->features[5] & LMP_SNIFF_SUBR)
#define lmp_esco_capable(dev) ((dev)->features[3] & LMP_ESCO)
#define lmp_ssp_capable(dev) ((dev)->features[6] & LMP_SIMPLE_PAIR)
+#define lmp_le_capable(dev) ((dev)->features[4] & LMP_LE)
/* ----- HCI protocols ----- */
struct hci_proto {
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index 30329ed..d230a46 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -324,6 +324,11 @@ int hci_conn_del(struct hci_conn *conn)
/* Unacked frames */
hdev->acl_cnt += conn->sent;
+ } else if (conn->type == LE_LINK) {
+ if (hdev->le_pkts)
+ hdev->le_cnt += conn->sent;
+ else
+ hdev->acl_cnt += conn->sent;
} else {
struct hci_conn *acl = conn->link;
if (acl) {
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index bc2a052..45c78c2 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -254,6 +254,14 @@ static void hci_init_req(struct hci_dev *hdev, unsigned long opt)
hci_send_cmd(hdev, HCI_OP_WRITE_CA_TIMEOUT, 2, ¶m);
}
+static void hci_le_init_req(struct hci_dev *hdev, unsigned long opt)
+{
+ BT_DBG("%s", hdev->name);
+
+ /* Read LE buffer size */
+ hci_send_cmd(hdev, HCI_OP_LE_READ_BUFFER_SIZE, 0, NULL);
+}
+
static void hci_scan_req(struct hci_dev *hdev, unsigned long opt)
{
__u8 scan = opt;
@@ -509,6 +517,10 @@ int hci_dev_open(__u16 dev)
ret = __hci_request(hdev, hci_init_req, 0,
msecs_to_jiffies(HCI_INIT_TIMEOUT));
+ if (lmp_le_capable(hdev))
+ ret = __hci_request(hdev, hci_le_init_req, 0,
+ msecs_to_jiffies(HCI_INIT_TIMEOUT));
+
clear_bit(HCI_INIT, &hdev->flags);
}
@@ -645,7 +657,7 @@ int hci_dev_reset(__u16 dev)
hdev->flush(hdev);
atomic_set(&hdev->cmd_cnt, 1);
- hdev->acl_cnt = 0; hdev->sco_cnt = 0;
+ hdev->acl_cnt = 0; hdev->sco_cnt = 0; hdev->le_cnt = 0;
if (!test_bit(HCI_RAW, &hdev->flags))
ret = __hci_request(hdev, hci_reset_req, 0,
@@ -1456,8 +1468,25 @@ static inline struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type, int
}
if (conn) {
- int cnt = (type == ACL_LINK ? hdev->acl_cnt : hdev->sco_cnt);
- int q = cnt / num;
+ int cnt, q;
+
+ switch (conn->type) {
+ case ACL_LINK:
+ cnt = hdev->acl_cnt;
+ break;
+ case SCO_LINK:
+ case ESCO_LINK:
+ cnt = hdev->sco_cnt;
+ break;
+ case LE_LINK:
+ cnt = hdev->le_mtu ? hdev->le_cnt : hdev->acl_cnt;
+ break;
+ default:
+ cnt = 0;
+ BT_ERR("Unknown link type");
+ }
+
+ q = cnt / num;
*quote = q ? q : 1;
} else
*quote = 0;
@@ -1556,6 +1585,40 @@ static inline void hci_sched_esco(struct hci_dev *hdev)
}
}
+static inline void hci_sched_le(struct hci_dev *hdev)
+{
+ struct hci_conn *conn;
+ struct sk_buff *skb;
+ int quote, cnt;
+
+ BT_DBG("%s", hdev->name);
+
+ if (!test_bit(HCI_RAW, &hdev->flags)) {
+ /* ACL tx timeout must be longer than maximum
+ * link supervision timeout (40.9 seconds) */
+ if (!hdev->le_cnt &&
+ time_after(jiffies, hdev->le_last_tx + HZ * 45))
+ hci_acl_tx_to(hdev);
+ }
+
+ cnt = hdev->le_pkts ? hdev->le_cnt : hdev->acl_cnt;
+ while (cnt && (conn = hci_low_sent(hdev, LE_LINK, "e))) {
+ while (quote-- && (skb = skb_dequeue(&conn->data_q))) {
+ BT_DBG("skb %p len %d", skb, skb->len);
+
+ hci_send_frame(skb);
+ hdev->le_last_tx = jiffies;
+
+ cnt--;
+ conn->sent++;
+ }
+ }
+ if (hdev->le_pkts)
+ hdev->le_cnt = cnt;
+ else
+ hdev->acl_cnt = cnt;
+}
+
static void hci_tx_task(unsigned long arg)
{
struct hci_dev *hdev = (struct hci_dev *) arg;
@@ -1563,7 +1626,8 @@ static void hci_tx_task(unsigned long arg)
read_lock(&hci_task_lock);
- BT_DBG("%s acl %d sco %d", hdev->name, hdev->acl_cnt, hdev->sco_cnt);
+ BT_DBG("%s acl %d sco %d le %d", hdev->name, hdev->acl_cnt,
+ hdev->sco_cnt, hdev->le_cnt);
/* Schedule queues and send stuff to HCI driver */
@@ -1573,6 +1637,8 @@ static void hci_tx_task(unsigned long arg)
hci_sched_esco(hdev);
+ hci_sched_le(hdev);
+
/* Send next queued raw (unknown type) packet */
while ((skb = skb_dequeue(&hdev->raw_q)))
hci_send_frame(skb);
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index 31cea58..6735804 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -539,6 +539,26 @@ static void hci_cc_read_bd_addr(struct hci_dev *hdev, struct sk_buff *skb)
hci_req_complete(hdev, rp->status);
}
+static void hci_cc_le_read_buffer_size(struct hci_dev *hdev,
+ struct sk_buff *skb)
+{
+ struct hci_rp_le_read_buffer_size *rp = (void *) skb->data;
+
+ BT_DBG("%s status 0x%x", hdev->name, rp->status);
+
+ if (rp->status)
+ return;
+
+ hdev->le_mtu = __le16_to_cpu(rp->le_mtu);
+ hdev->le_pkts = rp->le_max_pkt;
+
+ hdev->le_cnt = hdev->le_pkts;
+
+ BT_DBG("%s le mtu %d:%d", hdev->name, hdev->le_mtu, hdev->le_pkts);
+
+ hci_req_complete(hdev, rp->status);
+}
+
static inline void hci_cs_inquiry(struct hci_dev *hdev, __u8 status)
{
BT_DBG("%s status 0x%x", hdev->name, status);
@@ -1416,6 +1436,10 @@ static inline void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *sk
hci_cc_read_bd_addr(hdev, skb);
break;
+ case HCI_OP_LE_READ_BUFFER_SIZE:
+ hci_cc_le_read_buffer_size(hdev, skb);
+ break;
+
default:
BT_DBG("%s opcode 0x%x", hdev->name, opcode);
break;
@@ -1553,10 +1577,22 @@ static inline void hci_num_comp_pkts_evt(struct hci_dev *hdev, struct sk_buff *s
conn->sent -= count;
if (conn->type == ACL_LINK) {
- if ((hdev->acl_cnt += count) > hdev->acl_pkts)
+ hdev->acl_cnt += count;
+ if (hdev->acl_cnt > hdev->acl_pkts)
hdev->acl_cnt = hdev->acl_pkts;
+ } else if (conn->type == LE_LINK) {
+ if (hdev->le_pkts) {
+ hdev->le_cnt += count;
+ if (hdev->le_cnt > hdev->le_pkts)
+ hdev->le_cnt = hdev->le_pkts;
+ } else {
+ hdev->acl_cnt += count;
+ if (hdev->acl_cnt > hdev->acl_pkts)
+ hdev->acl_cnt = hdev->acl_pkts;
+ }
} else {
- if ((hdev->sco_cnt += count) > hdev->sco_pkts)
+ hdev->sco_cnt += count;
+ if (hdev->sco_cnt > hdev->sco_pkts)
hdev->sco_cnt = hdev->sco_pkts;
}
}
--
1.7.3.2
From: Ville Tervo <[email protected]>
Bluetooth V4.0 adds support for Low Energy (LE)
connections. Specification introduses new set
of hci commands to control LE connection.
This patch adds logic to create, cancel and
disconnect LE connections
Signed-off-by: Ville Tervo <[email protected]>
---
include/net/bluetooth/hci.h | 2 +
include/net/bluetooth/hci_core.h | 25 +++++++++--
net/bluetooth/hci_conn.c | 51 +++++++++++++++++++-
net/bluetooth/hci_event.c | 93 ++++++++++++++++++++++++++++++++++++++
4 files changed, 164 insertions(+), 7 deletions(-)
diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index ee5beec..02055b9 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -159,6 +159,8 @@ enum {
#define SCO_LINK 0x00
#define ACL_LINK 0x01
#define ESCO_LINK 0x02
+/* Low Energy links do not have defined link type. Use invented one */
+#define LE_LINK 0x80
/* LMP features */
#define LMP_3SLOT 0x01
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index ebec8c9..2b7f94a 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -60,6 +60,7 @@ struct hci_conn_hash {
spinlock_t lock;
unsigned int acl_num;
unsigned int sco_num;
+ unsigned int le_num;
};
struct bdaddr_list {
@@ -272,20 +273,36 @@ static inline void hci_conn_hash_add(struct hci_dev *hdev, struct hci_conn *c)
{
struct hci_conn_hash *h = &hdev->conn_hash;
list_add(&c->list, &h->list);
- if (c->type == ACL_LINK)
+ switch (c->type) {
+ case ACL_LINK:
h->acl_num++;
- else
+ break;
+ case LE_LINK:
+ h->le_num++;
+ break;
+ case SCO_LINK:
+ case ESCO_LINK:
h->sco_num++;
+ break;
+ }
}
static inline void hci_conn_hash_del(struct hci_dev *hdev, struct hci_conn *c)
{
struct hci_conn_hash *h = &hdev->conn_hash;
list_del(&c->list);
- if (c->type == ACL_LINK)
+ switch (c->type) {
+ case ACL_LINK:
h->acl_num--;
- else
+ break;
+ case LE_LINK:
+ h->le_num--;
+ break;
+ case SCO_LINK:
+ case ESCO_LINK:
h->sco_num--;
+ break;
+ }
}
static inline struct hci_conn *hci_conn_hash_lookup_handle(struct hci_dev *hdev,
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index 0b1e460..30329ed 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -45,6 +45,32 @@
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
+static void hci_le_connect(struct hci_conn *conn)
+{
+ struct hci_dev *hdev = conn->hdev;
+ struct hci_cp_le_create_conn cp;
+
+ conn->state = BT_CONNECT;
+ conn->out = 1;
+
+ memset(&cp, 0, sizeof(cp));
+ cp.scan_interval = cpu_to_le16(0x0004);
+ cp.scan_window = cpu_to_le16(0x0004);
+ bacpy(&cp.peer_addr, &conn->dst);
+ cp.conn_interval_min = cpu_to_le16(0x0008);
+ cp.conn_interval_max = cpu_to_le16(0x0100);
+ cp.supervision_timeout = cpu_to_le16(0x0064);
+ cp.min_ce_len = cpu_to_le16(0x0001);
+ cp.max_ce_len = cpu_to_le16(0x0001);
+
+ hci_send_cmd(hdev, HCI_OP_LE_CREATE_CONN, sizeof(cp), &cp);
+}
+
+static void hci_le_connect_cancel(struct hci_conn *conn)
+{
+ hci_send_cmd(conn->hdev, HCI_OP_LE_CREATE_CONN_CANCEL, 0, NULL);
+}
+
void hci_acl_connect(struct hci_conn *conn)
{
struct hci_dev *hdev = conn->hdev;
@@ -192,8 +218,12 @@ static void hci_conn_timeout(unsigned long arg)
switch (conn->state) {
case BT_CONNECT:
case BT_CONNECT2:
- if (conn->type == ACL_LINK && conn->out)
- hci_acl_connect_cancel(conn);
+ if (conn->out) {
+ if (conn->type == ACL_LINK)
+ hci_acl_connect_cancel(conn);
+ else if (conn->type == LE_LINK)
+ hci_le_connect_cancel(conn);
+ }
break;
case BT_CONFIG:
case BT_CONNECTED:
@@ -359,15 +389,30 @@ struct hci_dev *hci_get_route(bdaddr_t *dst, bdaddr_t *src)
}
EXPORT_SYMBOL(hci_get_route);
-/* Create SCO or ACL connection.
+/* Create SCO, ACL or LE connection.
* Device _must_ be locked */
struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8 sec_level, __u8 auth_type)
{
struct hci_conn *acl;
struct hci_conn *sco;
+ struct hci_conn *le;
BT_DBG("%s dst %s", hdev->name, batostr(dst));
+ if (type == LE_LINK) {
+ le = hci_conn_hash_lookup_ba(hdev, LE_LINK, dst);
+ if (!le)
+ le = hci_conn_add(hdev, LE_LINK, dst);
+ if (!le)
+ return NULL;
+ if (le->state == BT_OPEN)
+ hci_le_connect(le);
+
+ hci_conn_hold(le);
+
+ return le;
+ }
+
if (!(acl = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst))) {
if (!(acl = hci_conn_add(hdev, ACL_LINK, dst)))
return NULL;
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index 3c1957c..31cea58 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -863,6 +863,43 @@ static void hci_cs_exit_sniff_mode(struct hci_dev *hdev, __u8 status)
hci_dev_unlock(hdev);
}
+static void hci_cs_le_create_conn(struct hci_dev *hdev, __u8 status)
+{
+ struct hci_cp_le_create_conn *cp;
+ struct hci_conn *conn;
+
+ BT_DBG("%s status 0x%x", hdev->name, status);
+
+ cp = hci_sent_cmd_data(hdev, HCI_OP_LE_CREATE_CONN);
+ if (!cp)
+ return;
+
+ hci_dev_lock(hdev);
+
+ conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->peer_addr);
+
+ BT_DBG("%s bdaddr %s conn %p", hdev->name, batostr(&cp->peer_addr),
+ conn);
+
+ if (status) {
+ if (conn && conn->state == BT_CONNECT) {
+ conn->state = BT_CLOSED;
+ hci_proto_connect_cfm(conn, status);
+ hci_conn_del(conn);
+ }
+ } else {
+ if (!conn) {
+ conn = hci_conn_add(hdev, LE_LINK, &cp->peer_addr);
+ if (conn)
+ conn->out = 1;
+ else
+ BT_ERR("No memory for new connection");
+ }
+ }
+
+ hci_dev_unlock(hdev);
+}
+
static inline void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
{
__u8 status = *((__u8 *) skb->data);
@@ -1445,6 +1482,10 @@ static inline void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb)
hci_cs_exit_sniff_mode(hdev, ev->status);
break;
+ case HCI_OP_LE_CREATE_CONN:
+ hci_cs_le_create_conn(hdev, ev->status);
+ break;
+
default:
BT_DBG("%s opcode 0x%x", hdev->name, opcode);
break;
@@ -1898,6 +1939,54 @@ static inline void hci_remote_host_features_evt(struct hci_dev *hdev, struct sk_
hci_dev_unlock(hdev);
}
+static inline void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ struct hci_ev_le_conn_complete *ev = (void *) skb->data;
+ struct hci_conn *conn;
+
+ BT_DBG("%s status %d", hdev->name, ev->status);
+
+ hci_dev_lock(hdev);
+
+ conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &ev->bdaddr);
+ if (!conn)
+ goto unlock;
+
+ if (ev->status) {
+ hci_proto_connect_cfm(conn, ev->status);
+ conn->state = BT_CLOSED;
+ hci_conn_del(conn);
+ goto unlock;
+ }
+
+ conn->handle = __le16_to_cpu(ev->handle);
+ conn->state = BT_CONNECTED;
+
+ hci_conn_hold_device(conn);
+ hci_conn_add_sysfs(conn);
+
+ hci_proto_connect_cfm(conn, ev->status);
+
+unlock:
+ 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;
+
+ skb_pull(skb, sizeof(*le_ev));
+
+ switch (le_ev->subevent) {
+ case HCI_EV_LE_CONN_COMPLETE:
+ hci_le_conn_complete_evt(hdev, skb);
+ break;
+
+ default:
+ break;
+ }
+}
+
void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
{
struct hci_event_hdr *hdr = (void *) skb->data;
@@ -2034,6 +2123,10 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
hci_remote_host_features_evt(hdev, skb);
break;
+ case HCI_EV_LE_META:
+ hci_le_meta_evt(hdev, skb);
+ break;
+
default:
BT_DBG("%s event 0x%x", hdev->name, event);
break;
--
1.7.3.2
From: Ville Tervo <[email protected]>
Add needed HCI command and event structs to
create LE connections.
Signed-off-by: Ville Tervo <[email protected]>
---
include/net/bluetooth/hci.h | 49 +++++++++++++++++++++++++++++++++++++++++++
1 files changed, 49 insertions(+), 0 deletions(-)
diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index e30e008..ee5beec 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -593,6 +593,36 @@ struct hci_rp_read_bd_addr {
bdaddr_t bdaddr;
} __packed;
+#define HCI_OP_LE_SET_EVENT_MASK 0x2001
+struct hci_cp_le_set_event_mask {
+ __u8 mask[8];
+} __packed;
+
+#define HCI_OP_LE_READ_BUFFER_SIZE 0x2002
+struct hci_rp_le_read_buffer_size {
+ __u8 status;
+ __le16 le_mtu;
+ __u8 le_max_pkt;
+} __packed;
+
+#define HCI_OP_LE_CREATE_CONN 0x200d
+struct hci_cp_le_create_conn {
+ __le16 scan_interval;
+ __le16 scan_window;
+ __u8 filter_policy;
+ __u8 peer_addr_type;
+ bdaddr_t peer_addr;
+ __u8 own_address_type;
+ __le16 conn_interval_min;
+ __le16 conn_interval_max;
+ __le16 conn_latency;
+ __le16 supervision_timeout;
+ __le16 min_ce_len;
+ __le16 max_ce_len;
+} __packed;
+
+#define HCI_OP_LE_CREATE_CONN_CANCEL 0x200e
+
/* ---- HCI Events ---- */
#define HCI_EV_INQUIRY_COMPLETE 0x01
@@ -845,6 +875,25 @@ struct hci_ev_remote_host_features {
__u8 features[8];
} __packed;
+#define HCI_EV_LE_META 0x3e
+struct hci_ev_le_meta {
+ __u8 subevent;
+} __packed;
+
+/* Low energy meta events */
+#define HCI_EV_LE_CONN_COMPLETE 0x01
+struct hci_ev_le_conn_complete {
+ __u8 status;
+ __le16 handle;
+ __u8 role;
+ __u8 bdaddr_type;
+ bdaddr_t bdaddr;
+ __le16 interval;
+ __le16 latency;
+ __le16 supervision_timeout;
+ __u8 clk_accurancy;
+} __packed;
+
/* Internal events generated by Bluetooth stack */
#define HCI_EV_STACK_INTERNAL 0xfd
struct hci_ev_stack_internal {
--
1.7.3.2