2012-03-15 12:29:51

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFCv4 00/30] RFC Bluetooth A2MP implementation

From: Andrei Emeltchenko <[email protected]>

Changes:
* RFCv4: redesign code to use l2cap channel lock instead of socket lock
and general modifications. Basic implementation of HCI callback interface.
* RFCv3: redesign code to use l2cap functions instead of kernel sockets
L2CAP functions modified to work with channels without sk.
* RFCv2: rebased against "workqueue" patches.
* RFCv1: added refcnt to amp_mgr, fixed sleeping in atomic

Some code based of reference implementations below.

References: Code Aurora (git://codeaurora.org/kernel/msm.git) and Atheros
(search for: [PATCH 2/3] Add a2mp protocol/AMP manager, by Atheros Linux BT3)
implementations.

Andrei Emeltchenko (30):
Bluetooth: Make ertm_init available
Bluetooth: Add send function to chan ops
Bluetooth: Make l2cap_chan_add available
Bluetooth: Add set_err to state_change callback
Bluetooth: Lock sk only if exist
Bluetooth: Timers fixes
Bluetooth: A2MP: Create A2MP channel
Bluetooth: A2MP: AMP Manager basic functions
Bluetooth: A2MP: Add channel close callback
Bluetooth: Add state_change for A2MP channel
Bluetooth: A2MP: Build and Send msg helpers
Bluetooth: A2MP: skb allocation callback
Bluetooth: A2MP: Definitions for A2MP commands
Bluetooth: A2MP: Define A2MP status codes
Bluetooth: A2MP: Process A2MP messages
Bluetooth: A2MP: Process A2MP Command Reject
Bluetooth: A2MP: Helper functions to count HCI devs
Bluetooth: A2MP: Process A2MP Discover Request
Bluetooth: A2MP: Process A2MP Change Notify
Bluetooth: A2MP: Process A2MP Get Info Request
Bluetooth: A2MP: Process A2MP Get AMP Assoc Request
Bluetooth: A2MP: Process A2MP Create Physlink Request
Bluetooth: A2MP: Process A2MP Disc Physlink Request
Bluetooth: A2MP: Process A2MP Command Responses
Bluetooth: A2MP: Handling fixed channels
Bluetooth: A2MP: Manage incoming connections
Bluetooth: physical link HCI interface to AMP
Bluetooth: Define AMP controller statuses
Bluetooth: General HCI callback implementation
Bluetooth: Process HCI callbacks in a workqueue

include/net/bluetooth/a2mp.h | 125 +++++++++
include/net/bluetooth/hci.h | 35 +++
include/net/bluetooth/hci_core.h | 36 +++
include/net/bluetooth/l2cap.h | 38 ++-
net/bluetooth/a2mp.c | 533 ++++++++++++++++++++++++++++++++++++++
net/bluetooth/hci_conn.c | 15 +
net/bluetooth/hci_core.c | 168 ++++++++++++
net/bluetooth/l2cap_core.c | 92 ++++---
net/bluetooth/l2cap_sock.c | 8 +-
9 files changed, 997 insertions(+), 53 deletions(-)
create mode 100644 include/net/bluetooth/a2mp.h
create mode 100644 net/bluetooth/a2mp.c

--
1.7.9.1



2012-03-20 13:00:26

by Andrei Emeltchenko

[permalink] [raw]
Subject: Re: [RFCv4 06/30] Bluetooth: Timers fixes

Hi Ulisses,

On Tue, Mar 20, 2012 at 09:21:56AM -0300, Ulisses Furquim wrote:
> > -static inline void l2cap_set_timer(struct l2cap_chan *chan,
> > - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? struct delayed_work *work, long
> > timeout)
> > -{
> > - ? ? ? BT_DBG("chan %p state %s timeout %ld", chan,
> > - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? state_to_string(chan->state),
> > timeout);
> > -
> > - ? ? ? if (!cancel_delayed_work(work))
> > - ? ? ? ? ? ? ? l2cap_chan_hold(chan);
> > - ? ? ? schedule_delayed_work(work, timeout);
> > -}
> > -
> > ?static inline bool l2cap_clear_timer(struct l2cap_chan *chan,
> > ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?struct delayed_work *work)
> > ?{
> > ? ? ? ?bool ret;
> >
> > - ? ? ? ret = cancel_delayed_work(work);
> > + ? ? ? ret = (delayed_work_pending(work) && cancel_delayed_work(work));
> > ? ? ? ?if (ret)
> > ? ? ? ? ? ? ? ?l2cap_chan_put(chan);
> >
> > ? ? ? ?return ret;
> > ?}
> >
> > +static inline void l2cap_set_timer(struct l2cap_chan *chan,
> > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? struct delayed_work *work, long
> > timeout)
> > +{
> > + ? ? ? BT_DBG("chan %p state %s timeout %ld", chan,
> > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? state_to_string(chan->state),
> > timeout);
> > +
> > + ? ? ? l2cap_clear_timer(chan, work);
> > +
> > + ? ? ? l2cap_chan_hold(chan);
> > + ? ? ? schedule_delayed_work(work, timeout);
> > +}
> > +
> What are you fixing here? Care to explain and send an example? This
> needs to be clear in the commit message so we don't have more
> confusion with timer handling as we had this code for quite some time.

I have just sent the patch with proper commit message. I think you assume
that cancel_delayed_work returns not zero if it cancels delayed work but
it returns not zero if there is no work running (even if there was no timer
-- as far as I understood and comment apply).

Best regards
Andrei Emeltchenko


2012-03-20 12:52:22

by Ulisses Furquim

[permalink] [raw]
Subject: Re: [RFCv4 05/30] Bluetooth: Lock sk only if exist

Hi Andrei,

On Tue, Mar 20, 2012 at 9:49 AM, Andrei Emeltchenko
<[email protected]> wrote:
> Hi Ulisses,
>
> On Tue, Mar 20, 2012 at 09:22:46AM -0300, Ulisses Furquim wrote:
>> > @@ -212,9 +212,13 @@ static inline void l2cap_state_change(struct l2cap_chan *chan, int state, int er
>> > ?{
>> > ? ? ? ?struct sock *sk = chan->sk;
>> >
>> > - ? ? ? lock_sock(sk);
>> > + ? ? ? if (sk)
>> > + ? ? ? ? ? ? ? lock_sock(sk);
>> > +
>> > ? ? ? ?__l2cap_state_change(chan, state, err);
>> > - ? ? ? release_sock(sk);
>> > +
>> > + ? ? ? if (sk)
>> > + ? ? ? ? ? ? ? release_sock(sk);
>> > ?}
>> >
>> > ?void __l2cap_chan_set_err(struct l2cap_chan *chan, int err)
>> > --
>> > 1.7.9.1
>>
>> Well, this doesn't look good, does it? Wouldn't make sense to call
>> __l2cap_state_change() where we know sk doesn't exist and
>> l2cap_state_change() in the others? After all the separation between
>> chan and sk is something we need to have as much clear as possible
>> from now on, right?
>
> Sounds good, the only issue is that instead of this simple change we would
> have dozens of "if/else".

If we'll have dozens of "if/else" then the separation is still not
good IMO. Right?

Regards,

--
Ulisses Furquim
ProFUSION embedded systems
http://profusion.mobi
Mobile: +55 19 9250 0942
Skype: ulissesffs

2012-03-20 12:49:26

by Andrei Emeltchenko

[permalink] [raw]
Subject: Re: [RFCv4 05/30] Bluetooth: Lock sk only if exist

Hi Ulisses,

On Tue, Mar 20, 2012 at 09:22:46AM -0300, Ulisses Furquim wrote:
> > @@ -212,9 +212,13 @@ static inline void l2cap_state_change(struct l2cap_chan *chan, int state, int er
> > ?{
> > ? ? ? ?struct sock *sk = chan->sk;
> >
> > - ? ? ? lock_sock(sk);
> > + ? ? ? if (sk)
> > + ? ? ? ? ? ? ? lock_sock(sk);
> > +
> > ? ? ? ?__l2cap_state_change(chan, state, err);
> > - ? ? ? release_sock(sk);
> > +
> > + ? ? ? if (sk)
> > + ? ? ? ? ? ? ? release_sock(sk);
> > ?}
> >
> > ?void __l2cap_chan_set_err(struct l2cap_chan *chan, int err)
> > --
> > 1.7.9.1
>
> Well, this doesn't look good, does it? Wouldn't make sense to call
> __l2cap_state_change() where we know sk doesn't exist and
> l2cap_state_change() in the others? After all the separation between
> chan and sk is something we need to have as much clear as possible
> from now on, right?

Sounds good, the only issue is that instead of this simple change we would
have dozens of "if/else".

Best regards
Andrei Emeltchenko

2012-03-20 12:22:46

by Ulisses Furquim

[permalink] [raw]
Subject: Re: [RFCv4 05/30] Bluetooth: Lock sk only if exist

Hi Andrei,

On Thu, Mar 15, 2012 at 9:29 AM, Andrei Emeltchenko
<[email protected]> wrote:
> From: Andrei Emeltchenko <[email protected]>
>
>
> Signed-off-by: Andrei Emeltchenko <[email protected]>
> ---
> ?net/bluetooth/l2cap_core.c | ? ?8 ++++++--
> ?1 files changed, 6 insertions(+), 2 deletions(-)
>
> diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
> index 779f819..e8855cc 100644
> --- a/net/bluetooth/l2cap_core.c
> +++ b/net/bluetooth/l2cap_core.c
> @@ -212,9 +212,13 @@ static inline void l2cap_state_change(struct l2cap_chan *chan, int state, int er
> ?{
> ? ? ? ?struct sock *sk = chan->sk;
>
> - ? ? ? lock_sock(sk);
> + ? ? ? if (sk)
> + ? ? ? ? ? ? ? lock_sock(sk);
> +
> ? ? ? ?__l2cap_state_change(chan, state, err);
> - ? ? ? release_sock(sk);
> +
> + ? ? ? if (sk)
> + ? ? ? ? ? ? ? release_sock(sk);
> ?}
>
> ?void __l2cap_chan_set_err(struct l2cap_chan *chan, int err)
> --
> 1.7.9.1

Well, this doesn't look good, does it? Wouldn't make sense to call
__l2cap_state_change() where we know sk doesn't exist and
l2cap_state_change() in the others? After all the separation between
chan and sk is something we need to have as much clear as possible
from now on, right?

Regards,

--
Ulisses Furquim
ProFUSION embedded systems
http://profusion.mobi
Mobile: +55 19 9250 0942
Skype: ulissesffs

2012-03-20 12:21:56

by Ulisses Furquim

[permalink] [raw]
Subject: Re: [RFCv4 06/30] Bluetooth: Timers fixes

Hi Andrei,

On Thu, Mar 15, 2012 at 9:29 AM, Andrei Emeltchenko
<[email protected]> wrote:
>
> From: Andrei Emeltchenko <[email protected]>
>
>
> Signed-off-by: Andrei Emeltchenko <[email protected]>
> ---
> ?include/net/bluetooth/l2cap.h | ? 25 +++++++++++++------------
> ?1 files changed, 13 insertions(+), 12 deletions(-)
>
> diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
> index d9c668c..1a5c9e6 100644
> --- a/include/net/bluetooth/l2cap.h
> +++ b/include/net/bluetooth/l2cap.h
> @@ -624,29 +624,30 @@ static inline void l2cap_chan_unlock(struct
> l2cap_chan *chan)
> ? ? ? ?mutex_unlock(&chan->lock);
> ?}
>
> -static inline void l2cap_set_timer(struct l2cap_chan *chan,
> - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? struct delayed_work *work, long
> timeout)
> -{
> - ? ? ? BT_DBG("chan %p state %s timeout %ld", chan,
> - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? state_to_string(chan->state),
> timeout);
> -
> - ? ? ? if (!cancel_delayed_work(work))
> - ? ? ? ? ? ? ? l2cap_chan_hold(chan);
> - ? ? ? schedule_delayed_work(work, timeout);
> -}
> -
> ?static inline bool l2cap_clear_timer(struct l2cap_chan *chan,
> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?struct delayed_work *work)
> ?{
> ? ? ? ?bool ret;
>
> - ? ? ? ret = cancel_delayed_work(work);
> + ? ? ? ret = (delayed_work_pending(work) && cancel_delayed_work(work));
> ? ? ? ?if (ret)
> ? ? ? ? ? ? ? ?l2cap_chan_put(chan);
>
> ? ? ? ?return ret;
> ?}
>
> +static inline void l2cap_set_timer(struct l2cap_chan *chan,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? struct delayed_work *work, long
> timeout)
> +{
> + ? ? ? BT_DBG("chan %p state %s timeout %ld", chan,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? state_to_string(chan->state),
> timeout);
> +
> + ? ? ? l2cap_clear_timer(chan, work);
> +
> + ? ? ? l2cap_chan_hold(chan);
> + ? ? ? schedule_delayed_work(work, timeout);
> +}
> +
> ?#define __set_chan_timer(c, t) l2cap_set_timer(c, &c->chan_timer, (t))
> ?#define __clear_chan_timer(c) l2cap_clear_timer(c, &c->chan_timer)
> ?#define __set_retrans_timer(c) l2cap_set_timer(c, &c->retrans_timer, \
> --
> 1.7.9.1

What are you fixing here? Care to explain and send an example? This
needs to be clear in the commit message so we don't have more
confusion with timer handling as we had this code for quite some time.

Regards,

--
Ulisses Furquim
ProFUSION embedded systems
http://profusion.mobi
Mobile: +55 19 9250 0942
Skype: ulissesffs

2012-03-20 08:03:43

by Andrei Emeltchenko

[permalink] [raw]
Subject: Re: [RFCv4 01/30] Bluetooth: Make ertm_init available

Hi Gustavo,

On Tue, Mar 20, 2012 at 12:04:49AM -0300, Gustavo Padovan wrote:
> Hi Andrei,
>
> > -static inline void l2cap_ertm_init(struct l2cap_chan *chan)
> > +void l2cap_ertm_init(struct l2cap_chan *chan)
>
> I prefer to see this kind of change with the actual code that uses it.

I will merge it.

Best regards
Andrei Emeltchenko

2012-03-20 03:10:32

by Gustavo Padovan

[permalink] [raw]
Subject: Re: [RFCv4 06/30] Bluetooth: Timers fixes

Hi Andrei,

* Andrei Emeltchenko <[email protected]> [2012-03-15 14:29:57 +0200]:

> From: Andrei Emeltchenko <[email protected]>
>
>
> Signed-off-by: Andrei Emeltchenko <[email protected]>
> ---
> include/net/bluetooth/l2cap.h | 25 +++++++++++++------------
> 1 files changed, 13 insertions(+), 12 deletions(-)
>
> diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
> index d9c668c..1a5c9e6 100644
> --- a/include/net/bluetooth/l2cap.h
> +++ b/include/net/bluetooth/l2cap.h
> @@ -624,29 +624,30 @@ static inline void l2cap_chan_unlock(struct l2cap_chan *chan)
> mutex_unlock(&chan->lock);
> }
>
> -static inline void l2cap_set_timer(struct l2cap_chan *chan,
> - struct delayed_work *work, long timeout)
> -{
> - BT_DBG("chan %p state %s timeout %ld", chan,
> - state_to_string(chan->state), timeout);
> -
> - if (!cancel_delayed_work(work))
> - l2cap_chan_hold(chan);
> - schedule_delayed_work(work, timeout);
> -}
> -
> static inline bool l2cap_clear_timer(struct l2cap_chan *chan,
> struct delayed_work *work)
> {
> bool ret;
>
> - ret = cancel_delayed_work(work);
> + ret = (delayed_work_pending(work) && cancel_delayed_work(work));
> if (ret)
> l2cap_chan_put(chan);
>
> return ret;
> }
>
> +static inline void l2cap_set_timer(struct l2cap_chan *chan,
> + struct delayed_work *work, long timeout)
> +{
> + BT_DBG("chan %p state %s timeout %ld", chan,
> + state_to_string(chan->state), timeout);
> +
> + l2cap_clear_timer(chan, work);
> +
> + l2cap_chan_hold(chan);
> + schedule_delayed_work(work, timeout);
> +}
> +

This one looks good, but you need to improve the title and commit message.
As it is a fix send it before the others.

Gustavo

2012-03-20 03:04:49

by Gustavo Padovan

[permalink] [raw]
Subject: Re: [RFCv4 01/30] Bluetooth: Make ertm_init available

Hi Andrei,

* Andrei Emeltchenko <[email protected]> [2012-03-15 14:29:52 +0200]:

> From: Andrei Emeltchenko <[email protected]>
>
> Function l2cap_ertm_init will be used for initialization
> fixed A2MP channel.
>
> Signed-off-by: Andrei Emeltchenko <[email protected]>
> ---
> include/net/bluetooth/l2cap.h | 1 +
> net/bluetooth/l2cap_core.c | 2 +-
> 2 files changed, 2 insertions(+), 1 deletions(-)
>
> diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
> index 9b242c6..c107944 100644
> --- a/include/net/bluetooth/l2cap.h
> +++ b/include/net/bluetooth/l2cap.h
> @@ -861,5 +861,6 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,
> u32 priority);
> void l2cap_chan_busy(struct l2cap_chan *chan, int busy);
> int l2cap_chan_check_security(struct l2cap_chan *chan);
> +void l2cap_ertm_init(struct l2cap_chan *chan);
>
> #endif /* __L2CAP_H */
> diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
> index 0a46603..b0d1cee 100644
> --- a/net/bluetooth/l2cap_core.c
> +++ b/net/bluetooth/l2cap_core.c
> @@ -2038,7 +2038,7 @@ static void l2cap_ack_timeout(struct work_struct *work)
> l2cap_chan_put(chan);
> }
>
> -static inline void l2cap_ertm_init(struct l2cap_chan *chan)
> +void l2cap_ertm_init(struct l2cap_chan *chan)

I prefer to see this kind of change with the actual code that uses it.

Gustavo

2012-03-20 03:03:47

by Gustavo Padovan

[permalink] [raw]
Subject: Re: [RFCv4 05/30] Bluetooth: Lock sk only if exist

Hi Andrei,

* Andrei Emeltchenko <[email protected]> [2012-03-15 14:29:56 +0200]:

> From: Andrei Emeltchenko <[email protected]>
>
>
> Signed-off-by: Andrei Emeltchenko <[email protected]>
> ---
> net/bluetooth/l2cap_core.c | 8 ++++++--
> 1 files changed, 6 insertions(+), 2 deletions(-)

We told about this several times, please include a commit message description
in all patches.

Gustavo

2012-03-17 23:12:59

by Gustavo Padovan

[permalink] [raw]
Subject: Re: [RFCv4 11/30] Bluetooth: A2MP: Build and Send msg helpers

Hi Andrei,

* Andrei Emeltchenko <[email protected]> [2012-03-15 14:30:02 +0200]:

> From: Andrei Emeltchenko <[email protected]>
>
> Helper function to build and send A2MP messages.
>
> Signed-off-by: Andrei Emeltchenko <[email protected]>
> ---
> include/net/bluetooth/a2mp.h | 7 ++++++
> net/bluetooth/a2mp.c | 49 ++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 56 insertions(+), 0 deletions(-)
>
> diff --git a/include/net/bluetooth/a2mp.h b/include/net/bluetooth/a2mp.h
> index 0fe8ddd..995f1c0 100644
> --- a/include/net/bluetooth/a2mp.h
> +++ b/include/net/bluetooth/a2mp.h
> @@ -24,6 +24,13 @@ struct amp_mgr {
> unsigned long flags;
> };
>
> +struct a2mp_cmd {
> + __u8 code;
> + __u8 ident;
> + __le16 len;
> + __u8 data[0];
> +} __packed;
> +
> void amp_mgr_get(struct amp_mgr *mgr);
> int amp_mgr_put(struct amp_mgr *mgr);
>
> diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
> index 63970b5..19a8ac3 100644
> --- a/net/bluetooth/a2mp.c
> +++ b/net/bluetooth/a2mp.c
> @@ -16,6 +16,54 @@
> #include <net/bluetooth/l2cap.h>
> #include <net/bluetooth/a2mp.h>
>
> +/* A2MP build & send command helper functions */
> +static struct a2mp_cmd *__a2mp_build(u8 code, u8 ident, u16 len, void *data)
> +{
> + struct a2mp_cmd *cmd;
> + int plen;
> +
> + plen = sizeof(*cmd) + len;
> + cmd = kzalloc(plen, GFP_KERNEL);
> + if (!cmd)
> + return NULL;
> +
> + cmd->code = code;
> + cmd->ident = ident;
> + cmd->len = cpu_to_le16(len);
> +
> + memcpy(cmd->data, data, len);
> +
> + return cmd;
> +}
> +
> +static inline int __a2mp_send(struct amp_mgr *mgr, u8 *data, int len)
> +{
> + struct l2cap_chan *chan = mgr->a2mp_chan;
> + struct kvec iv = { data, len };
> + struct msghdr msg;
> +
> + memset(&msg, 0, sizeof(msg));
> +
> + msg.msg_iov = (struct iovec *) &iv;
> + msg.msg_iovlen = 1;
> +
> + return chan->ops->send(chan, &msg, len, 0);

Why do you need this? Just call l2cap_chan_send() directly here.

Gustavo

2012-03-15 12:29:57

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFCv4 06/30] Bluetooth: Timers fixes

From: Andrei Emeltchenko <[email protected]>


Signed-off-by: Andrei Emeltchenko <[email protected]>
---
include/net/bluetooth/l2cap.h | 25 +++++++++++++------------
1 files changed, 13 insertions(+), 12 deletions(-)

diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
index d9c668c..1a5c9e6 100644
--- a/include/net/bluetooth/l2cap.h
+++ b/include/net/bluetooth/l2cap.h
@@ -624,29 +624,30 @@ static inline void l2cap_chan_unlock(struct l2cap_chan *chan)
mutex_unlock(&chan->lock);
}

-static inline void l2cap_set_timer(struct l2cap_chan *chan,
- struct delayed_work *work, long timeout)
-{
- BT_DBG("chan %p state %s timeout %ld", chan,
- state_to_string(chan->state), timeout);
-
- if (!cancel_delayed_work(work))
- l2cap_chan_hold(chan);
- schedule_delayed_work(work, timeout);
-}
-
static inline bool l2cap_clear_timer(struct l2cap_chan *chan,
struct delayed_work *work)
{
bool ret;

- ret = cancel_delayed_work(work);
+ ret = (delayed_work_pending(work) && cancel_delayed_work(work));
if (ret)
l2cap_chan_put(chan);

return ret;
}

+static inline void l2cap_set_timer(struct l2cap_chan *chan,
+ struct delayed_work *work, long timeout)
+{
+ BT_DBG("chan %p state %s timeout %ld", chan,
+ state_to_string(chan->state), timeout);
+
+ l2cap_clear_timer(chan, work);
+
+ l2cap_chan_hold(chan);
+ schedule_delayed_work(work, timeout);
+}
+
#define __set_chan_timer(c, t) l2cap_set_timer(c, &c->chan_timer, (t))
#define __clear_chan_timer(c) l2cap_clear_timer(c, &c->chan_timer)
#define __set_retrans_timer(c) l2cap_set_timer(c, &c->retrans_timer, \
--
1.7.9.1


2012-03-15 12:29:55

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFCv4 04/30] Bluetooth: Add set_err to state_change callback

From: Andrei Emeltchenko <[email protected]>

There are several places in the code where l2cap_state_change and
l2cap_chan_set_err used together. With combining them we remove
socket lock in l2cap_send_disconn_req and simplify code in couple
of other places with a price of adding extra parameter err to
state_change.

Signed-off-by: Andrei Emeltchenko <[email protected]>
---
include/net/bluetooth/l2cap.h | 4 ++-
net/bluetooth/l2cap_core.c | 56 ++++++++++++++++++----------------------
net/bluetooth/l2cap_sock.c | 8 +++++-
3 files changed, 35 insertions(+), 33 deletions(-)

diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
index f089162..d9c668c 100644
--- a/include/net/bluetooth/l2cap.h
+++ b/include/net/bluetooth/l2cap.h
@@ -508,7 +508,8 @@ struct l2cap_ops {
int (*send) (struct l2cap_chan *chan,
struct msghdr *msg, size_t len, u32 priority);
void (*close) (void *data);
- void (*state_change) (void *data, int state);
+ void (*state_change) (void *data, int state,
+ int err);
struct sk_buff *(*alloc_skb) (struct l2cap_chan *chan,
unsigned long len, int nb, int *err);

@@ -866,5 +867,6 @@ int l2cap_chan_check_security(struct l2cap_chan *chan);
void l2cap_ertm_init(struct l2cap_chan *chan);

void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan);
+void __l2cap_chan_set_err(struct l2cap_chan *chan, int err);

#endif /* __L2CAP_H */
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index b0d1cee..779f819 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -199,25 +199,25 @@ static u16 l2cap_alloc_cid(struct l2cap_conn *conn)
return 0;
}

-static void __l2cap_state_change(struct l2cap_chan *chan, int state)
+static void __l2cap_state_change(struct l2cap_chan *chan, int state, int err)
{
BT_DBG("chan %p %s -> %s", chan, state_to_string(chan->state),
state_to_string(state));

chan->state = state;
- chan->ops->state_change(chan->data, state);
+ chan->ops->state_change(chan->data, state, err);
}

-static void l2cap_state_change(struct l2cap_chan *chan, int state)
+static inline void l2cap_state_change(struct l2cap_chan *chan, int state, int err)
{
struct sock *sk = chan->sk;

lock_sock(sk);
- __l2cap_state_change(chan, state);
+ __l2cap_state_change(chan, state, err);
release_sock(sk);
}

-static inline void __l2cap_chan_set_err(struct l2cap_chan *chan, int err)
+void __l2cap_chan_set_err(struct l2cap_chan *chan, int err)
{
struct sock *sk = chan->sk;

@@ -377,11 +377,9 @@ static void l2cap_chan_del(struct l2cap_chan *chan, int err)

lock_sock(sk);

- __l2cap_state_change(chan, BT_CLOSED);
- sock_set_flag(sk, SOCK_ZAPPED);
+ __l2cap_state_change(chan, BT_CLOSED, err);

- if (err)
- __l2cap_chan_set_err(chan, err);
+ sock_set_flag(sk, SOCK_ZAPPED);

if (parent) {
bt_accept_unlink(sk);
@@ -445,7 +443,7 @@ void l2cap_chan_close(struct l2cap_chan *chan, int reason)
lock_sock(sk);
l2cap_chan_cleanup_listen(sk);

- __l2cap_state_change(chan, BT_CLOSED);
+ __l2cap_state_change(chan, BT_CLOSED, 0);
sock_set_flag(sk, SOCK_ZAPPED);
release_sock(sk);
break;
@@ -471,7 +469,7 @@ void l2cap_chan_close(struct l2cap_chan *chan, int reason)
result = L2CAP_CR_SEC_BLOCK;
else
result = L2CAP_CR_BAD_PSM;
- l2cap_state_change(chan, BT_DISCONN);
+ l2cap_state_change(chan, BT_DISCONN, 0);

rsp.scid = cpu_to_le16(chan->dcid);
rsp.dcid = cpu_to_le16(chan->scid);
@@ -725,7 +723,6 @@ static inline int l2cap_mode_supported(__u8 mode, __u32 feat_mask)

static void l2cap_send_disconn_req(struct l2cap_conn *conn, struct l2cap_chan *chan, int err)
{
- struct sock *sk = chan->sk;
struct l2cap_disconn_req req;

if (!conn)
@@ -742,10 +739,7 @@ static void l2cap_send_disconn_req(struct l2cap_conn *conn, struct l2cap_chan *c
l2cap_send_cmd(conn, l2cap_get_ident(conn),
L2CAP_DISCONN_REQ, sizeof(req), &req);

- lock_sock(sk);
- __l2cap_state_change(chan, BT_DISCONN);
- __l2cap_chan_set_err(chan, err);
- release_sock(sk);
+ l2cap_state_change(chan, BT_DISCONN, err);
}

/* ---- L2CAP connections ---- */
@@ -800,7 +794,7 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
parent->sk_data_ready(parent, 0);

} else {
- __l2cap_state_change(chan, BT_CONFIG);
+ __l2cap_state_change(chan, BT_CONFIG, 0);
rsp.result = cpu_to_le16(L2CAP_CR_SUCCESS);
rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO);
}
@@ -904,7 +898,7 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn)

__set_chan_timer(chan, sk->sk_sndtimeo);

- __l2cap_state_change(chan, BT_CONNECTED);
+ __l2cap_state_change(chan, BT_CONNECTED, 0);
parent->sk_data_ready(parent, 0);

clean:
@@ -925,7 +919,7 @@ static void l2cap_chan_ready(struct l2cap_chan *chan)
chan->conf_state = 0;
__clear_chan_timer(chan);

- __l2cap_state_change(chan, BT_CONNECTED);
+ __l2cap_state_change(chan, BT_CONNECTED, 0);
sk->sk_state_change(sk);

if (parent)
@@ -960,7 +954,7 @@ static void l2cap_conn_ready(struct l2cap_conn *conn)
struct sock *sk = chan->sk;
__clear_chan_timer(chan);
lock_sock(sk);
- __l2cap_state_change(chan, BT_CONNECTED);
+ __l2cap_state_change(chan, BT_CONNECTED, 0);
sk->sk_state_change(sk);
release_sock(sk);

@@ -1244,14 +1238,14 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, bdaddr_t *d
l2cap_chan_add(conn, chan);
l2cap_chan_lock(chan);

- l2cap_state_change(chan, BT_CONNECT);
+ l2cap_state_change(chan, BT_CONNECT, 0);
__set_chan_timer(chan, sk->sk_sndtimeo);

if (hcon->state == BT_CONNECTED) {
if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
__clear_chan_timer(chan);
if (l2cap_chan_check_security(chan))
- l2cap_state_change(chan, BT_CONNECTED);
+ l2cap_state_change(chan, BT_CONNECTED, 0);
} else
l2cap_do_start(chan);
}
@@ -2705,22 +2699,22 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd
if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE) {
if (l2cap_chan_check_security(chan)) {
if (bt_sk(sk)->defer_setup) {
- __l2cap_state_change(chan, BT_CONNECT2);
+ __l2cap_state_change(chan, BT_CONNECT2, 0);
result = L2CAP_CR_PEND;
status = L2CAP_CS_AUTHOR_PEND;
parent->sk_data_ready(parent, 0);
} else {
- __l2cap_state_change(chan, BT_CONFIG);
+ __l2cap_state_change(chan, BT_CONFIG, 0);
result = L2CAP_CR_SUCCESS;
status = L2CAP_CS_NO_INFO;
}
} else {
- __l2cap_state_change(chan, BT_CONNECT2);
+ __l2cap_state_change(chan, BT_CONNECT2, 0);
result = L2CAP_CR_PEND;
status = L2CAP_CS_AUTHEN_PEND;
}
} else {
- __l2cap_state_change(chan, BT_CONNECT2);
+ __l2cap_state_change(chan, BT_CONNECT2, 0);
result = L2CAP_CR_PEND;
status = L2CAP_CS_NO_INFO;
}
@@ -2799,7 +2793,7 @@ static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hd

switch (result) {
case L2CAP_CR_SUCCESS:
- l2cap_state_change(chan, BT_CONFIG);
+ l2cap_state_change(chan, BT_CONFIG, 0);
chan->ident = 0;
chan->dcid = dcid;
clear_bit(CONF_CONNECT_PEND, &chan->conf_state);
@@ -2911,7 +2905,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr
if (test_bit(CONF_INPUT_DONE, &chan->conf_state)) {
set_default_fcs(chan);

- l2cap_state_change(chan, BT_CONNECTED);
+ l2cap_state_change(chan, BT_CONNECTED, 0);

chan->next_tx_seq = 0;
chan->expected_tx_seq = 0;
@@ -3042,7 +3036,7 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr
if (test_bit(CONF_OUTPUT_DONE, &chan->conf_state)) {
set_default_fcs(chan);

- l2cap_state_change(chan, BT_CONNECTED);
+ l2cap_state_change(chan, BT_CONNECTED, 0);
chan->next_tx_seq = 0;
chan->expected_tx_seq = 0;
skb_queue_head_init(&chan->tx_q);
@@ -4614,12 +4608,12 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
if (parent)
parent->sk_data_ready(parent, 0);
} else {
- __l2cap_state_change(chan, BT_CONFIG);
+ __l2cap_state_change(chan, BT_CONFIG, 0);
res = L2CAP_CR_SUCCESS;
stat = L2CAP_CS_NO_INFO;
}
} else {
- __l2cap_state_change(chan, BT_DISCONN);
+ __l2cap_state_change(chan, BT_DISCONN, 0);
__set_chan_timer(chan, L2CAP_DISC_TIMEOUT);
res = L2CAP_CR_SEC_BLOCK;
stat = L2CAP_CS_NO_INFO;
diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
index 29122ed..7e26d63 100644
--- a/net/bluetooth/l2cap_sock.c
+++ b/net/bluetooth/l2cap_sock.c
@@ -919,11 +919,17 @@ static void l2cap_sock_close_cb(void *data)
l2cap_sock_kill(sk);
}

-static void l2cap_sock_state_change_cb(void *data, int state)
+static void l2cap_sock_state_change_cb(void *data, int state, int err)
{
struct sock *sk = data;

sk->sk_state = state;
+
+ if (err) {
+ struct l2cap_chan *chan = l2cap_pi(sk)->chan;
+
+ __l2cap_chan_set_err(chan, err);
+ }
}

static struct sk_buff *l2cap_sock_alloc_skb_cb(struct l2cap_chan *chan,
--
1.7.9.1


2012-03-15 12:29:58

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFCv4 07/30] Bluetooth: A2MP: Create A2MP channel

From: Andrei Emeltchenko <[email protected]>

Create fixed A2MP channel

Signed-off-by: Andrei Emeltchenko <[email protected]>
---
include/net/bluetooth/l2cap.h | 3 ++
net/bluetooth/a2mp.c | 63 +++++++++++++++++++++++++++++++++++++++++
2 files changed, 66 insertions(+), 0 deletions(-)
create mode 100644 net/bluetooth/a2mp.c

diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
index 1a5c9e6..b3f9854 100644
--- a/include/net/bluetooth/l2cap.h
+++ b/include/net/bluetooth/l2cap.h
@@ -51,6 +51,8 @@
#define L2CAP_CONN_TIMEOUT msecs_to_jiffies(40000)
#define L2CAP_INFO_TIMEOUT msecs_to_jiffies(4000)

+#define L2CAP_A2MP_DEFAULT_MTU 670
+
/* L2CAP socket address */
struct sockaddr_l2 {
sa_family_t l2_family;
@@ -224,6 +226,7 @@ struct l2cap_conn_rsp {
/* channel indentifier */
#define L2CAP_CID_SIGNALING 0x0001
#define L2CAP_CID_CONN_LESS 0x0002
+#define L2CAP_CID_A2MP 0x0003
#define L2CAP_CID_LE_DATA 0x0004
#define L2CAP_CID_LE_SIGNALING 0x0005
#define L2CAP_CID_SMP 0x0006
diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
new file mode 100644
index 0000000..52bb7d8
--- /dev/null
+++ b/net/bluetooth/a2mp.c
@@ -0,0 +1,63 @@
+/*
+ Copyright (c) 2010,2011 Code Aurora Forum. All rights reserved.
+ Copyright (c) 2011,2012 Intel Corp.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License version 2 and
+ only version 2 as published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/l2cap.h>
+
+static struct l2cap_ops a2mp_chan_ops = {
+ .name = "L2CAP A2MP channel",
+};
+
+static struct l2cap_chan *open_a2mp_chan(struct l2cap_conn *conn)
+{
+ struct l2cap_chan *chan;
+
+ chan = l2cap_chan_create(NULL);
+
+ hci_conn_hold(conn->hcon);
+
+ BT_DBG("chan %p", chan);
+
+ chan->sec_level = BT_SECURITY_LOW;
+ chan->omtu = L2CAP_A2MP_DEFAULT_MTU;
+ chan->imtu = L2CAP_A2MP_DEFAULT_MTU;
+ chan->flush_to = L2CAP_DEFAULT_FLUSH_TO;
+ chan->fcs = L2CAP_FCS_CRC16;
+
+ chan->ops = &a2mp_chan_ops;
+
+ set_bit(FLAG_FORCE_ACTIVE, &chan->flags);
+
+ chan->max_tx = 0xFF;
+ chan->remote_max_tx = chan->max_tx;
+
+ /* Send 10 packets without ack */
+ chan->tx_win = 10;
+ chan->tx_win_max = L2CAP_DEFAULT_TX_WINDOW;
+ chan->remote_tx_win = chan->tx_win;
+
+ chan->retrans_timeout = L2CAP_DEFAULT_RETRANS_TO;
+ chan->monitor_timeout = L2CAP_DEFAULT_MONITOR_TO;
+
+ skb_queue_head_init(&chan->tx_q);
+
+ chan->mode = L2CAP_MODE_ERTM;
+ l2cap_ertm_init(chan);
+
+ l2cap_chan_add(conn, chan);
+ chan->remote_mps = chan->omtu;
+ chan->mps = chan->omtu;
+
+ return chan;
+}
--
1.7.9.1


2012-03-15 12:30:02

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFCv4 11/30] Bluetooth: A2MP: Build and Send msg helpers

From: Andrei Emeltchenko <[email protected]>

Helper function to build and send A2MP messages.

Signed-off-by: Andrei Emeltchenko <[email protected]>
---
include/net/bluetooth/a2mp.h | 7 ++++++
net/bluetooth/a2mp.c | 49 ++++++++++++++++++++++++++++++++++++++++++
2 files changed, 56 insertions(+), 0 deletions(-)

diff --git a/include/net/bluetooth/a2mp.h b/include/net/bluetooth/a2mp.h
index 0fe8ddd..995f1c0 100644
--- a/include/net/bluetooth/a2mp.h
+++ b/include/net/bluetooth/a2mp.h
@@ -24,6 +24,13 @@ struct amp_mgr {
unsigned long flags;
};

+struct a2mp_cmd {
+ __u8 code;
+ __u8 ident;
+ __le16 len;
+ __u8 data[0];
+} __packed;
+
void amp_mgr_get(struct amp_mgr *mgr);
int amp_mgr_put(struct amp_mgr *mgr);

diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index 63970b5..19a8ac3 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -16,6 +16,54 @@
#include <net/bluetooth/l2cap.h>
#include <net/bluetooth/a2mp.h>

+/* A2MP build & send command helper functions */
+static struct a2mp_cmd *__a2mp_build(u8 code, u8 ident, u16 len, void *data)
+{
+ struct a2mp_cmd *cmd;
+ int plen;
+
+ plen = sizeof(*cmd) + len;
+ cmd = kzalloc(plen, GFP_KERNEL);
+ if (!cmd)
+ return NULL;
+
+ cmd->code = code;
+ cmd->ident = ident;
+ cmd->len = cpu_to_le16(len);
+
+ memcpy(cmd->data, data, len);
+
+ return cmd;
+}
+
+static inline int __a2mp_send(struct amp_mgr *mgr, u8 *data, int len)
+{
+ struct l2cap_chan *chan = mgr->a2mp_chan;
+ struct kvec iv = { data, len };
+ struct msghdr msg;
+
+ memset(&msg, 0, sizeof(msg));
+
+ msg.msg_iov = (struct iovec *) &iv;
+ msg.msg_iovlen = 1;
+
+ return chan->ops->send(chan, &msg, len, 0);
+}
+
+static void a2mp_send(struct amp_mgr *mgr, u8 code, u8 ident, u16 len,
+ void *data)
+{
+ struct a2mp_cmd *cmd;
+
+ cmd = __a2mp_build(code, ident, len, data);
+ if (!cmd)
+ return;
+
+ __a2mp_send(mgr, (u8 *)cmd, len + sizeof(*cmd));
+
+ kfree(cmd);
+}
+
static void a2mp_chan_state_change_cb(void *data, int state, int err)
{
struct l2cap_chan *chan = data;
@@ -44,6 +92,7 @@ static void a2mp_chan_close_cb(void *data)
static struct l2cap_ops a2mp_chan_ops = {
.name = "L2CAP A2MP channel",
.close = a2mp_chan_close_cb,
+ .send = l2cap_chan_send,
.state_change = a2mp_chan_state_change_cb,
};

--
1.7.9.1


2012-03-15 12:30:01

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFCv4 10/30] Bluetooth: Add state_change for A2MP channel

From: Andrei Emeltchenko <[email protected]>

Remove AMP Manager when A2MP channel closed.

Signed-off-by: Andrei Emeltchenko <[email protected]>
---
net/bluetooth/a2mp.c | 21 +++++++++++++++++++++
1 files changed, 21 insertions(+), 0 deletions(-)

diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index 5945788..63970b5 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -16,6 +16,24 @@
#include <net/bluetooth/l2cap.h>
#include <net/bluetooth/a2mp.h>

+static void a2mp_chan_state_change_cb(void *data, int state, int err)
+{
+ struct l2cap_chan *chan = data;
+ struct amp_mgr *mgr;
+
+ BT_DBG("chan %p state %s", chan, state_to_string(state));
+
+ chan->state = state;
+
+ switch (state) {
+ case BT_CLOSED:
+ mgr = chan->data;
+ if (mgr)
+ amp_mgr_put(mgr);
+ break;
+ }
+}
+
static void a2mp_chan_close_cb(void *data)
{
struct amp_mgr *mgr = data;
@@ -26,6 +44,7 @@ static void a2mp_chan_close_cb(void *data)
static struct l2cap_ops a2mp_chan_ops = {
.name = "L2CAP A2MP channel",
.close = a2mp_chan_close_cb,
+ .state_change = a2mp_chan_state_change_cb,
};

static struct l2cap_chan *open_a2mp_chan(struct l2cap_conn *conn)
@@ -68,6 +87,8 @@ static struct l2cap_chan *open_a2mp_chan(struct l2cap_conn *conn)
chan->remote_mps = chan->omtu;
chan->mps = chan->omtu;

+ chan->ops->state_change(chan, BT_CONNECTED, 0);
+
return chan;
}

--
1.7.9.1


2012-03-15 12:30:09

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFCv4 18/30] Bluetooth: A2MP: Process A2MP Discover Request

From: Andrei Emeltchenko <[email protected]>

Process A2MP Discover Request, code makes sure that first
controller in the list is BREDR one. Trace is shown below:
...
> ACL data: handle 11 flags 0x02 dlen 16
A2MP: Discover req: mtu/mps 670 mask: 0x0000
< ACL data: handle 11 flags 0x00 dlen 19
A2MP: Discover rsp: mtu/mps 670 mask: 0x0000
Controller list:
id 0, type 0, status 0x01 (Bluetooth only)
...

Signed-off-by: Andrei Emeltchenko <[email protected]>
---
include/net/bluetooth/a2mp.h | 2 +
net/bluetooth/a2mp.c | 55 ++++++++++++++++++++++++++++++++++++++++++
2 files changed, 57 insertions(+), 0 deletions(-)

diff --git a/include/net/bluetooth/a2mp.h b/include/net/bluetooth/a2mp.h
index c99c375..a748ab0 100644
--- a/include/net/bluetooth/a2mp.h
+++ b/include/net/bluetooth/a2mp.h
@@ -15,6 +15,8 @@
#ifndef __A2MP_H
#define __A2MP_H

+#define A2MP_FEAT_EXT 0x8000
+
struct amp_mgr {
struct list_head list;
struct l2cap_conn *l2cap_conn;
diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index ac5084c..612df03 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -111,6 +111,58 @@ static inline int a2mp_command_rej(struct amp_mgr *mgr, struct sk_buff *skb,
return 0;
}

+static inline int a2mp_discover_req(struct amp_mgr *mgr, struct sk_buff *skb,
+ struct a2mp_cmd *hdr)
+{
+ struct a2mp_discov_req *req = (struct a2mp_discov_req *)skb->data;
+ struct a2mp_discov_rsp *rsp;
+ u16 ext_feat;
+ size_t len;
+ u8 num_ctrl;
+
+ if (le16_to_cpu(hdr->len) < sizeof(*req))
+ return -EINVAL;
+
+ skb_pull(skb, sizeof(*req));
+
+ BT_DBG("mtu %d efm 0x%4.4x", le16_to_cpu(req->mtu),
+ le16_to_cpu(req->ext_feat));
+
+ ext_feat = le16_to_cpu(req->ext_feat);
+
+ /* check that packet is not broken for now */
+ while (ext_feat & A2MP_FEAT_EXT) {
+ if (skb->len < sizeof(ext_feat))
+ return -EINVAL;
+
+ ext_feat = get_unaligned_le16(skb->data);
+ BT_DBG("ext_feat 0x%4.4x", le16_to_cpu(req->ext_feat));
+ skb_pull(skb, sizeof(ext_feat));
+ }
+
+ read_lock(&hci_dev_list_lock);
+
+ num_ctrl = __hci_num_ctrl();
+ len = num_ctrl * sizeof(struct a2mp_cl) + sizeof(*rsp);
+ rsp = kmalloc(len, GFP_ATOMIC);
+ if (!rsp) {
+ read_unlock(&hci_dev_list_lock);
+ return -ENOMEM;
+ }
+
+ rsp->mtu = cpu_to_le16(L2CAP_A2MP_DEFAULT_MTU);
+ rsp->ext_feat = 0;
+
+ __a2mp_add_cl(mgr, rsp->cl, num_ctrl);
+
+ read_unlock(&hci_dev_list_lock);
+
+ a2mp_send(mgr, A2MP_DISCOVER_RSP, hdr->ident, len, rsp);
+
+ kfree(rsp);
+ return 0;
+}
+
/* Handle A2MP signalling */
static int a2mp_chan_recv_cb(void *data, struct sk_buff *skb)
{
@@ -141,6 +193,9 @@ static int a2mp_chan_recv_cb(void *data, struct sk_buff *skb)
break;

case A2MP_DISCOVER_REQ:
+ err = a2mp_discover_req(mgr, skb, hdr);
+ break;
+
case A2MP_CHANGE_NOTIFY:
case A2MP_GETINFO_REQ:
case A2MP_GETAMPASSOC_REQ:
--
1.7.9.1


2012-03-15 12:30:11

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFCv4 20/30] Bluetooth: A2MP: Process A2MP Get Info Request

From: Andrei Emeltchenko <[email protected]>

Example of trace log for invalid controller id is shown below:
...
> ACL data: handle 11 flags 0x02 dlen 13
A2MP: Get Info req: id 238
< ACL data: handle 11 flags 0x00 dlen 30
A2MP: Get Info rsp: id 238 status (1) Invalid Controller ID
total bandwidth -381650496
max guaranteed bandwidth -274362188
min latency -158843144
pal capabilities 0x9750
assoc size 49478
...
Note that If the Status field is set to Invalid Controller ID all subsequent
fields in the AMP Get Info Response shall be ignored by the receiver.

Signed-off-by: Andrei Emeltchenko <[email protected]>
---
net/bluetooth/a2mp.c | 37 +++++++++++++++++++++++++++++++++++++
1 files changed, 37 insertions(+), 0 deletions(-)

diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index 4b8e369..96d4dfe 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -12,6 +12,8 @@
GNU General Public License for more details.
*/

+#include <linux/module.h>
+
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include <net/bluetooth/l2cap.h>
@@ -179,6 +181,38 @@ static inline int a2mp_change_notify(struct amp_mgr *mgr, struct sk_buff *skb,
return 0;
}

+static inline int a2mp_getinfo_req(struct amp_mgr *mgr, struct sk_buff *skb,
+ struct a2mp_cmd *hdr)
+{
+ struct a2mp_info_req *req = (struct a2mp_info_req *)skb->data;
+ struct hci_dev *hdev;
+ struct a2mp_info_rsp rsp;
+
+ if (le16_to_cpu(hdr->len) < sizeof(*req))
+ return -EINVAL;
+
+ rsp.id = req->id;
+ rsp.status = A2MP_STATUS_INVALID_CTRL_ID;
+
+ hdev = hci_dev_get(req->id);
+ if (hdev && hdev->amp_type != HCI_BREDR) {
+ rsp.status = 0;
+ rsp.total_bw = cpu_to_le32(hdev->amp_total_bw);
+ rsp.max_bw = cpu_to_le32(hdev->amp_max_bw);
+ rsp.min_latency = cpu_to_le32(hdev->amp_min_latency);
+ rsp.pal_cap = cpu_to_le16(hdev->amp_pal_cap);
+ rsp.assoc_size = cpu_to_le16(hdev->amp_assoc_size);
+ }
+
+ if (hdev)
+ hci_dev_put(hdev);
+
+ a2mp_send(mgr, A2MP_GETINFO_RSP, hdr->ident, sizeof(rsp), &rsp);
+
+ skb_pull(skb, sizeof(*req));
+ return 0;
+}
+
/* Handle A2MP signalling */
static int a2mp_chan_recv_cb(void *data, struct sk_buff *skb)
{
@@ -217,6 +251,9 @@ static int a2mp_chan_recv_cb(void *data, struct sk_buff *skb)
break;

case A2MP_GETINFO_REQ:
+ err = a2mp_getinfo_req(mgr, skb, hdr);
+ break;
+
case A2MP_GETAMPASSOC_REQ:
case A2MP_CREATEPHYSLINK_REQ:
case A2MP_DISCONNPHYSLINK_REQ:
--
1.7.9.1


2012-03-15 12:30:10

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFCv4 19/30] Bluetooth: A2MP: Process A2MP Change Notify

From: Andrei Emeltchenko <[email protected]>


Signed-off-by: Andrei Emeltchenko <[email protected]>
---
net/bluetooth/a2mp.c | 19 +++++++++++++++++++
1 files changed, 19 insertions(+), 0 deletions(-)

diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index 612df03..4b8e369 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -163,6 +163,22 @@ static inline int a2mp_discover_req(struct amp_mgr *mgr, struct sk_buff *skb,
return 0;
}

+static inline int a2mp_change_notify(struct amp_mgr *mgr, struct sk_buff *skb,
+ struct a2mp_cmd *hdr)
+{
+ struct a2mp_cl *cl = (struct a2mp_cl *)skb->data;
+
+ while (skb->len >= sizeof(*cl)) {
+ BT_DBG("Controller id %d type %d status %d", cl->id, cl->type,
+ cl->status);
+ cl = (struct a2mp_cl *) skb_pull(skb, sizeof(*cl));
+ }
+
+ /* TODO send A2MP_CHANGE_RSP */
+
+ return 0;
+}
+
/* Handle A2MP signalling */
static int a2mp_chan_recv_cb(void *data, struct sk_buff *skb)
{
@@ -197,6 +213,9 @@ static int a2mp_chan_recv_cb(void *data, struct sk_buff *skb)
break;

case A2MP_CHANGE_NOTIFY:
+ err = a2mp_change_notify(mgr, skb, hdr);
+ break;
+
case A2MP_GETINFO_REQ:
case A2MP_GETAMPASSOC_REQ:
case A2MP_CREATEPHYSLINK_REQ:
--
1.7.9.1


2012-03-15 12:29:54

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFCv4 03/30] Bluetooth: Make l2cap_chan_add available

From: Andrei Emeltchenko <[email protected]>

Function will be used when creating fixed channels

Signed-off-by: Andrei Emeltchenko <[email protected]>
---
include/net/bluetooth/l2cap.h | 2 ++
1 files changed, 2 insertions(+), 0 deletions(-)

diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
index 260f93e..f089162 100644
--- a/include/net/bluetooth/l2cap.h
+++ b/include/net/bluetooth/l2cap.h
@@ -865,4 +865,6 @@ void l2cap_chan_busy(struct l2cap_chan *chan, int busy);
int l2cap_chan_check_security(struct l2cap_chan *chan);
void l2cap_ertm_init(struct l2cap_chan *chan);

+void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan);
+
#endif /* __L2CAP_H */
--
1.7.9.1


2012-03-15 12:30:19

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFCv4 28/30] Bluetooth: Define AMP controller statuses

From: Andrei Emeltchenko <[email protected]>

AMP status codes copied from Bluez patch sent by Peter Krystad
<[email protected]>.

Signed-off-by: Andrei Emeltchenko <[email protected]>
---
include/net/bluetooth/hci.h | 9 +++++++++
1 files changed, 9 insertions(+), 0 deletions(-)

diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index 2096898..b642038 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -60,6 +60,15 @@

#define HCI_BREDR_ID 0

+/* AMP controller status */
+#define AMP_CTRL_POWERED_DOWN 0x00
+#define AMP_CTRL_BLUETOOTH_ONLY 0x01
+#define AMP_CTRL_NO_CAPACITY 0x02
+#define AMP_CTRL_LOW_CAPACITY 0x03
+#define AMP_CTRL_MEDIUM_CAPACITY 0x04
+#define AMP_CTRL_HIGH_CAPACITY 0x05
+#define AMP_CTRL_FULL_CAPACITY 0x06
+
/* HCI device quirks */
enum {
HCI_QUIRK_NO_RESET,
--
1.7.9.1


2012-03-15 12:29:59

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFCv4 08/30] Bluetooth: A2MP: AMP Manager basic functions

From: Andrei Emeltchenko <[email protected]>

Define AMP Manager and some basic functions.

Signed-off-by: Andrei Emeltchenko <[email protected]>
---
include/net/bluetooth/a2mp.h | 30 +++++++++++++++++++++
include/net/bluetooth/hci_core.h | 1 +
net/bluetooth/a2mp.c | 53 ++++++++++++++++++++++++++++++++++++++
net/bluetooth/hci_conn.c | 15 ++++++++++
4 files changed, 99 insertions(+), 0 deletions(-)
create mode 100644 include/net/bluetooth/a2mp.h

diff --git a/include/net/bluetooth/a2mp.h b/include/net/bluetooth/a2mp.h
new file mode 100644
index 0000000..0fe8ddd
--- /dev/null
+++ b/include/net/bluetooth/a2mp.h
@@ -0,0 +1,30 @@
+/*
+ Copyright (c) 2010,2011 Code Aurora Forum. All rights reserved.
+ Copyright (c) 2011,2012 Intel Corp.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License version 2 and
+ only version 2 as published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+#ifndef __A2MP_H
+#define __A2MP_H
+
+struct amp_mgr {
+ struct list_head list;
+ struct l2cap_conn *l2cap_conn;
+ struct l2cap_chan *a2mp_chan;
+ struct kref kref;
+ __u8 ident;
+ unsigned long flags;
+};
+
+void amp_mgr_get(struct amp_mgr *mgr);
+int amp_mgr_put(struct amp_mgr *mgr);
+
+#endif /* __A2MP_H */
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index 74dfefa..828fc7b 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -319,6 +319,7 @@ struct hci_conn {

struct sk_buff_head data_q;
struct list_head chan_list;
+ struct list_head mgr_list;

struct delayed_work disc_work;
struct timer_list idle_timer;
diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index 52bb7d8..66edcfa 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -14,6 +14,7 @@

#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/l2cap.h>
+#include <net/bluetooth/a2mp.h>

static struct l2cap_ops a2mp_chan_ops = {
.name = "L2CAP A2MP channel",
@@ -61,3 +62,55 @@ static struct l2cap_chan *open_a2mp_chan(struct l2cap_conn *conn)

return chan;
}
+
+/* AMP Manager functions */
+void amp_mgr_get(struct amp_mgr *mgr)
+{
+ BT_DBG("mgr %p", mgr);
+
+ kref_get(&mgr->kref);
+}
+
+static void amp_mgr_destroy(struct kref *kref)
+{
+ struct amp_mgr *mgr;
+ mgr = container_of(kref, struct amp_mgr, kref);
+
+ BT_DBG("mgr %p", mgr);
+
+ kfree(mgr);
+}
+
+int amp_mgr_put(struct amp_mgr *mgr)
+{
+ BT_DBG("mgr %p", mgr);
+
+ return kref_put(&mgr->kref, &amp_mgr_destroy);
+}
+
+static struct amp_mgr *amp_mgr_create(struct l2cap_conn *conn)
+{
+ struct amp_mgr *mgr;
+
+ mgr = kzalloc(sizeof(*mgr), GFP_KERNEL);
+ if (!mgr)
+ return NULL;
+
+ BT_DBG("conn %p mgr %p", conn, mgr);
+
+ mgr->l2cap_conn = conn;
+
+ mgr->a2mp_chan = open_a2mp_chan(conn);
+ if (!mgr->a2mp_chan) {
+ kfree(mgr);
+ return NULL;
+ }
+
+ mgr->a2mp_chan->data = mgr;
+
+ list_add(&mgr->list, &conn->hcon->mgr_list);
+
+ kref_init(&mgr->kref);
+
+ return mgr;
+}
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index 2a96211..472cf32 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -43,6 +43,7 @@

#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
+#include <net/bluetooth/a2mp.h>

static void hci_le_connect(struct hci_conn *conn)
{
@@ -407,6 +408,8 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)

INIT_LIST_HEAD(&conn->chan_list);

+ INIT_LIST_HEAD(&conn->mgr_list);
+
INIT_DELAYED_WORK(&conn->disc_work, hci_conn_timeout);
setup_timer(&conn->idle_timer, hci_conn_idle, (unsigned long)conn);
setup_timer(&conn->auto_accept_timer, hci_conn_auto_accept,
@@ -427,6 +430,16 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
return conn;
}

+static void hci_amp_mgr_list_flush(struct hci_conn *conn)
+{
+ struct amp_mgr *mgr, *n;
+
+ BT_DBG("conn %p", conn);
+
+ list_for_each_entry_safe(mgr, n, &conn->mgr_list, list)
+ amp_mgr_put(mgr);
+}
+
int hci_conn_del(struct hci_conn *conn)
{
struct hci_dev *hdev = conn->hdev;
@@ -462,6 +475,8 @@ int hci_conn_del(struct hci_conn *conn)

hci_chan_list_flush(conn);

+ hci_amp_mgr_list_flush(conn);
+
hci_conn_hash_del(hdev, conn);
if (hdev->notify)
hdev->notify(hdev, HCI_NOTIFY_CONN_DEL);
--
1.7.9.1


2012-03-15 12:30:06

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFCv4 15/30] Bluetooth: A2MP: Process A2MP messages

From: Andrei Emeltchenko <[email protected]>

Implement basic processing for AMP Manager Protocol (A2MP).

Example below shows processing unrecognized command.
...
> ACL data: handle 11 flags 0x02 dlen 12
A2MP: code 0x00 ident 3 len 0
< ACL data: handle 11 flags 0x00 dlen 14
A2MP: Command Reject: reason (0) - Command not recognized
...

Signed-off-by: Andrei Emeltchenko <[email protected]>
---
net/bluetooth/a2mp.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 59 insertions(+), 0 deletions(-)

diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index 1607592..b9b14e5 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -64,6 +64,64 @@ static void a2mp_send(struct amp_mgr *mgr, u8 code, u8 ident, u16 len,
kfree(cmd);
}

+/* Handle A2MP signalling */
+static int a2mp_chan_recv_cb(void *data, struct sk_buff *skb)
+{
+ struct a2mp_cmd *hdr = (struct a2mp_cmd *) skb->data;
+ struct amp_mgr *mgr = data;
+ int err = 0;
+
+ amp_mgr_get(mgr);
+
+ while (skb->len >= sizeof(*hdr)) {
+ struct a2mp_cmd *hdr = (struct a2mp_cmd *) skb->data;
+ u16 len = le16_to_cpu(hdr->len);
+
+ BT_DBG("code 0x%02x id %d len %d", hdr->code, hdr->ident, len);
+
+ skb_pull(skb, sizeof(*hdr));
+
+ if (len > skb->len || !hdr->ident) {
+ err = -EINVAL;
+ break;
+ }
+
+ mgr->ident = hdr->ident;
+
+ switch (hdr->code) {
+ case A2MP_COMMAND_REJ:
+ case A2MP_DISCOVER_REQ:
+ case A2MP_CHANGE_NOTIFY:
+ case A2MP_GETINFO_REQ:
+ case A2MP_GETAMPASSOC_REQ:
+ case A2MP_CREATEPHYSLINK_REQ:
+ case A2MP_DISCONNPHYSLINK_REQ:
+ case A2MP_CHANGE_RSP:
+ case A2MP_DISCOVER_RSP:
+ case A2MP_GETINFO_RSP:
+ case A2MP_GETAMPASSOC_RSP:
+ case A2MP_CREATEPHYSLINK_RSP:
+ case A2MP_DISCONNPHYSLINK_RSP:
+ default:
+ BT_ERR("Unknown A2MP sig cmd 0x%2.2x", hdr->code);
+ err = -EINVAL;
+ break;
+ }
+ }
+
+ if (err) {
+ struct a2mp_cmd_rej rej;
+ rej.reason = cpu_to_le16(0);
+
+ a2mp_send(mgr, A2MP_COMMAND_REJ, hdr->ident, sizeof(rej),
+ &rej);
+ }
+
+ amp_mgr_put(mgr);
+
+ return err;
+}
+
static void a2mp_chan_state_change_cb(void *data, int state, int err)
{
struct l2cap_chan *chan = data;
@@ -97,6 +155,7 @@ static struct sk_buff *a2mp_chan_alloc_skb_cb(struct l2cap_chan *chan,

static struct l2cap_ops a2mp_chan_ops = {
.name = "L2CAP A2MP channel",
+ .recv = a2mp_chan_recv_cb,
.close = a2mp_chan_close_cb,
.send = l2cap_chan_send,
.state_change = a2mp_chan_state_change_cb,
--
1.7.9.1


2012-03-15 12:30:17

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFCv4 26/30] Bluetooth: A2MP: Manage incoming connections

From: Andrei Emeltchenko <[email protected]>

Handle incoming A2MP connection by creating AMP manager and
processing A2MP messages.

Signed-off-by: Andrei Emeltchenko <[email protected]>
---
include/net/bluetooth/a2mp.h | 4 ++++
net/bluetooth/a2mp.c | 12 ++++++++++++
net/bluetooth/l2cap_core.c | 13 +++++++++----
3 files changed, 25 insertions(+), 4 deletions(-)

diff --git a/include/net/bluetooth/a2mp.h b/include/net/bluetooth/a2mp.h
index a748ab0..b8325489 100644
--- a/include/net/bluetooth/a2mp.h
+++ b/include/net/bluetooth/a2mp.h
@@ -15,6 +15,8 @@
#ifndef __A2MP_H
#define __A2MP_H

+#include <net/bluetooth/l2cap.h>
+
#define A2MP_FEAT_EXT 0x8000

struct amp_mgr {
@@ -117,5 +119,7 @@ struct a2mp_physlink_rsp {

void amp_mgr_get(struct amp_mgr *mgr);
int amp_mgr_put(struct amp_mgr *mgr);
+struct l2cap_chan *a2mp_channel_create(struct l2cap_conn *conn,
+ struct sk_buff *skb);

#endif /* __A2MP_H */
diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index 7709fcd..342b762 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -519,3 +519,15 @@ static struct amp_mgr *amp_mgr_create(struct l2cap_conn *conn)

return mgr;
}
+
+struct l2cap_chan *a2mp_channel_create(struct l2cap_conn *conn,
+ struct sk_buff *skb)
+{
+ struct amp_mgr *mgr;
+
+ mgr = amp_mgr_create(conn);
+
+ BT_DBG("mgr: %p chan %p", mgr, mgr->a2mp_chan);
+
+ return mgr->a2mp_chan;
+}
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index d840aae..ebdd909 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -56,6 +56,7 @@
#include <net/bluetooth/hci_core.h>
#include <net/bluetooth/l2cap.h>
#include <net/bluetooth/smp.h>
+#include <net/bluetooth/a2mp.h>

bool disable_ertm;

@@ -4296,10 +4297,14 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk

chan = l2cap_get_chan_by_scid(conn, cid);
if (!chan) {
- BT_DBG("unknown cid 0x%4.4x", cid);
- /* Drop packet and return */
- kfree_skb(skb);
- return 0;
+ if (cid == L2CAP_CID_A2MP) {
+ chan = a2mp_channel_create(conn, skb);
+ } else {
+ BT_DBG("unknown cid 0x%4.4x", cid);
+ /* Drop packet and return */
+ kfree_skb(skb);
+ return 0;
+ }
}

l2cap_chan_lock(chan);
--
1.7.9.1


2012-03-15 12:30:21

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFCv4 30/30] Bluetooth: Process HCI callbacks in a workqueue

From: Andrei Emeltchenko <[email protected]>

Conflicts:

include/net/bluetooth/hci_core.h
net/bluetooth/hci_core.c

Signed-off-by: Andrei Emeltchenko <[email protected]>
---
include/net/bluetooth/hci_core.h | 2 +
net/bluetooth/hci_core.c | 41 ++++++++++++++++++++++++++++++++++++++
2 files changed, 43 insertions(+), 0 deletions(-)

diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index 9dd93d2..a774712 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -1105,5 +1105,7 @@ int hci_cmd_cb(struct hci_dev *hdev, __u16 opcode, __u32 plen, void *param,
void (*cb)(struct hci_dev *hdev, struct hci_cb_cmd *cmd), void *opt,
void (*destructor)(struct hci_cb_cmd *cmd), gfp_t flags);
void hci_remove_cb(struct hci_dev *hdev, struct hci_cb_cmd *cmd);
+void hci_queue_cb(struct hci_dev *hdev, struct hci_cb_cmd *cmd,
+ struct workqueue_struct *workqueue);

#endif /* __HCI_CORE_H */
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index d8b443b..438486e 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -2278,6 +2278,47 @@ struct hci_cb_cmd *hci_find_cb(struct hci_dev *hdev, __u16 opcode)
return NULL;
}

+struct hci_cb_work {
+ struct work_struct work;
+ struct hci_dev *hdev;
+ struct hci_cb_cmd *cmd;
+};
+
+static void hci_cb_worker(struct work_struct *w)
+{
+ struct hci_cb_work *work = (struct hci_cb_work *) w;
+ struct hci_cb_cmd *cmd = work->cmd;
+ struct hci_dev *hdev = work->hdev;
+
+ cmd->cb(hdev, cmd);
+
+ hci_remove_cb(hdev, cmd);
+ kfree(w);
+ hci_dev_put(hdev);
+}
+
+void hci_queue_cb(struct hci_dev *hdev, struct hci_cb_cmd *cmd,
+ struct workqueue_struct *workqueue)
+{
+ struct hci_cb_work *work;
+
+ BT_DBG("Queue cmd %p opt %p", cmd, cmd->opt);
+
+ work = kmalloc(sizeof(*work), GFP_KERNEL);
+ if (!work)
+ return;
+
+ INIT_WORK(&work->work, hci_cb_worker);
+ work->hdev = hdev;
+ work->cmd = cmd;
+ hci_dev_hold(hdev);
+
+ if (!queue_work(workqueue, &work->work)) {
+ kfree(work);
+ hci_dev_put(hdev);
+ }
+}
+
void hci_remove_cb(struct hci_dev *hdev, struct hci_cb_cmd *cmd)
{
mutex_lock(&hdev->cb_list_lock);
--
1.7.9.1


2012-03-15 12:30:18

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFCv4 27/30] Bluetooth: physical link HCI interface to AMP

From: Andrei Emeltchenko <[email protected]>

Adds support for physical link create/acceppt/disconnect AMP HCI
commands. To be used by the upper layer.
Backport from CodeAurora & Atheros

Signed-off-by: Andrei Emeltchenko <[email protected]>
---
include/net/bluetooth/hci.h | 24 ++++++++++++++++++++++
net/bluetooth/hci_core.c | 45 +++++++++++++++++++++++++++++++++++++++++++
2 files changed, 69 insertions(+), 0 deletions(-)

diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index 852e9e2..2096898 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -30,6 +30,8 @@
#define HCI_MAX_EVENT_SIZE 260
#define HCI_MAX_FRAME_SIZE (HCI_MAX_ACL_SIZE + 4)

+#define HCI_MAX_AMP_KEY_SIZE 32
+
/* HCI dev events */
#define HCI_DEV_REG 1
#define HCI_DEV_UNREG 2
@@ -521,6 +523,28 @@ struct hci_cp_io_capability_neg_reply {
__u8 reason;
} __packed;

+#define HCI_OP_CREATE_PHY_LINK 0x0435
+struct hci_cp_create_phy_link {
+ __u8 handle;
+ __u8 key_len;
+ __u8 key_type;
+ __u8 key[HCI_MAX_AMP_KEY_SIZE];
+} __packed;
+
+#define HCI_OP_ACCEPT_PHY_LINK 0x0436
+struct hci_cp_accept_phy_link {
+ __u8 handle;
+ __u8 key_len;
+ __u8 key_type;
+ __u8 key[HCI_MAX_AMP_KEY_SIZE];
+} __packed;
+
+#define HCI_OP_DISC_PHY_LINK 0x0437
+struct hci_cp_disc_phy_link {
+ __u8 handle;
+ __u8 reason;
+} __packed;
+
#define HCI_OP_SNIFF_MODE 0x0803
struct hci_cp_sniff_mode {
__le16 handle;
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index 4b52c3e..eb362ba 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -337,6 +337,51 @@ static void hci_linkpol_req(struct hci_dev *hdev, unsigned long opt)
hci_send_cmd(hdev, HCI_OP_WRITE_DEF_LINK_POLICY, 2, &policy);
}

+/* AMP HCI interface */
+void hci_phylink_create_req(struct hci_dev *hdev, __u8 handle, __u8 key_len,
+ __u8 key_type, __u8 *key)
+{
+ struct hci_cp_create_phy_link cp;
+
+ cp.handle = handle;
+ cp.key_type = key_type;
+ cp.key_len = min_t(__u8, key_len, HCI_MAX_AMP_KEY_SIZE);
+
+ BT_DBG("key len %d, phy handle %d", cp.key_len, cp.handle);
+
+ memcpy(cp.key, key, cp.key_len);
+ hci_send_cmd(hdev, HCI_OP_CREATE_PHY_LINK, sizeof(cp), &cp);
+}
+EXPORT_SYMBOL(hci_phylink_create_req);
+
+void hci_phylink_accept_req(struct hci_dev *hdev, __u8 handle, __u8 key_len,
+ __u8 key_type, __u8 *key)
+{
+ struct hci_cp_accept_phy_link cp;
+
+ cp.handle = handle;
+ cp.key_type = key_type;
+ cp.key_len = min_t(__u8, key_len, HCI_MAX_AMP_KEY_SIZE);
+
+ BT_DBG("key len %d, phy handle %d", cp.key_len, cp.handle);
+
+ memcpy(cp.key, key, cp.key_len);
+ hci_send_cmd(hdev, HCI_OP_ACCEPT_PHY_LINK, sizeof(cp), &cp);
+}
+EXPORT_SYMBOL(hci_phylink_accept_req);
+
+void hci_phylink_disc_req(struct hci_dev *hdev, __u8 handle, __u8 reason)
+{
+ struct hci_cp_disc_phy_link cp;
+
+ BT_DBG("handle %d reason %d", handle, reason);
+
+ cp.handle = handle;
+ cp.reason = reason;
+ hci_send_cmd(hdev, HCI_OP_DISC_PHY_LINK, sizeof(cp), &cp);
+}
+EXPORT_SYMBOL(hci_phylink_disc_req);
+
/* Get HCI device by index.
* Device is held on return. */
struct hci_dev *hci_dev_get(int index)
--
1.7.9.1


2012-03-15 12:30:20

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFCv4 29/30] Bluetooth: General HCI callback implementation

From: Andrei Emeltchenko <[email protected]>

Add general HCI callback implementation. Can be used for executing
HCI commands from A2MP protocol.

Signed-off-by: Andrei Emeltchenko <[email protected]>
---
include/net/bluetooth/hci_core.h | 20 +++++++++
net/bluetooth/hci_core.c | 82 ++++++++++++++++++++++++++++++++++++++
2 files changed, 102 insertions(+), 0 deletions(-)

diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index 18433d0..9dd93d2 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -131,6 +131,17 @@ struct le_scan_params {

#define HCI_MAX_SHORT_NAME_LENGTH 10

+struct hci_dev;
+
+struct hci_cb_cmd {
+ struct list_head list;
+ u16 opcode;
+ u8 status;
+ void *opt;
+ void (*cb)(struct hci_dev *hdev, struct hci_cb_cmd *cmd);
+ void (*destructor)(struct hci_cb_cmd *cmd);
+};
+
#define NUM_REASSEMBLY 4
struct hci_dev {
struct list_head list;
@@ -238,6 +249,9 @@ struct hci_dev {

struct list_head mgmt_pending;

+ struct mutex cb_list_lock;
+ struct list_head cb_list;
+
struct discovery_state discovery;
struct hci_conn_hash conn_hash;
struct list_head blacklist;
@@ -1086,4 +1100,10 @@ int hci_cancel_inquiry(struct hci_dev *hdev);
int hci_le_scan(struct hci_dev *hdev, u8 type, u16 interval, u16 window,
int timeout);

+struct hci_cb_cmd *hci_find_cb(struct hci_dev *hdev, __u16 opcode);
+int hci_cmd_cb(struct hci_dev *hdev, __u16 opcode, __u32 plen, void *param,
+ void (*cb)(struct hci_dev *hdev, struct hci_cb_cmd *cmd), void *opt,
+ void (*destructor)(struct hci_cb_cmd *cmd), gfp_t flags);
+void hci_remove_cb(struct hci_dev *hdev, struct hci_cb_cmd *cmd);
+
#endif /* __HCI_CORE_H */
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index eb362ba..d8b443b 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -57,6 +57,7 @@
static void hci_rx_work(struct work_struct *work);
static void hci_cmd_work(struct work_struct *work);
static void hci_tx_work(struct work_struct *work);
+static void hci_cb_clear(struct hci_dev *hdev);

/* HCI device list */
LIST_HEAD(hci_dev_list);
@@ -1822,6 +1823,9 @@ int hci_register_dev(struct hci_dev *hdev)

INIT_LIST_HEAD(&hdev->mgmt_pending);

+ INIT_LIST_HEAD(&hdev->cb_list);
+ mutex_init(&hdev->cb_list_lock);
+
INIT_LIST_HEAD(&hdev->blacklist);

INIT_LIST_HEAD(&hdev->uuids);
@@ -1936,6 +1940,7 @@ void hci_unregister_dev(struct hci_dev *hdev)
hci_smp_ltks_clear(hdev);
hci_remote_oob_data_clear(hdev);
hci_adv_entries_clear(hdev);
+ hci_cb_clear(hdev);
hci_dev_unlock(hdev);

hci_dev_put(hdev);
@@ -2235,6 +2240,83 @@ int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen, void *param)
return 0;
}

+static int hci_add_cb(struct hci_dev *hdev, __u16 opcode,
+ void (*cb)(struct hci_dev *hdev, struct hci_cb_cmd *cmd),
+ void *opt, void (*destructor)(struct hci_cb_cmd *cmd), gfp_t flags)
+{
+ struct hci_cb_cmd *cmd;
+
+ cmd = kmalloc(sizeof(*cmd), flags);
+ if (!cmd)
+ return -ENOMEM;
+
+ cmd->cb = cb;
+ cmd->opcode = opcode;
+ cmd->opt = opt;
+ cmd->status = 0;
+ cmd->destructor = destructor;
+
+ mutex_lock(&hdev->cb_list_lock);
+ list_add(&cmd->list, &hdev->cb_list);
+ mutex_unlock(&hdev->cb_list_lock);
+
+ return 0;
+}
+
+struct hci_cb_cmd *hci_find_cb(struct hci_dev *hdev, __u16 opcode)
+{
+ struct hci_cb_cmd *cmd;
+
+ mutex_lock(&hdev->cb_list_lock);
+ list_for_each_entry(cmd, &hdev->cb_list, list)
+ if (cmd->opcode == opcode) {
+ mutex_unlock(&hdev->cb_list_lock);
+ return cmd;
+ }
+ mutex_unlock(&hdev->cb_list_lock);
+
+ return NULL;
+}
+
+void hci_remove_cb(struct hci_dev *hdev, struct hci_cb_cmd *cmd)
+{
+ mutex_lock(&hdev->cb_list_lock);
+ list_del(&cmd->list);
+ mutex_unlock(&hdev->cb_list_lock);
+
+ if (cmd->destructor) {
+ cmd->destructor(cmd);
+ } else {
+ kfree(cmd->opt);
+ kfree(cmd);
+ }
+}
+
+static void hci_cb_clear(struct hci_dev *hdev)
+{
+ struct hci_cb_cmd *cmd, *tmp;
+
+ list_for_each_entry_safe(cmd, tmp, &hdev->cb_list, list)
+ hci_remove_cb(hdev, cmd);
+}
+
+/* Send HCI command with callback */
+int hci_cmd_cb(struct hci_dev *hdev, __u16 opcode, __u32 plen, void *param,
+ void (*cb)(struct hci_dev *hdev, struct hci_cb_cmd *cmd),
+ void *opt, void (*destructor)(struct hci_cb_cmd *cmd), gfp_t flags)
+{
+ int ret;
+
+ if (!cb)
+ return -EINVAL;
+
+ ret = hci_add_cb(hdev, opcode, cb, opt, destructor, flags);
+ if (ret)
+ return ret;
+
+ return hci_send_cmd(hdev, opcode, plen, param);
+}
+
/* Get data from the previously sent command */
void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 opcode)
{
--
1.7.9.1


2012-03-15 12:30:14

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFCv4 23/30] Bluetooth: A2MP: Process A2MP Disc Physlink Request

From: Andrei Emeltchenko <[email protected]>


Signed-off-by: Andrei Emeltchenko <[email protected]>
---
net/bluetooth/a2mp.c | 37 +++++++++++++++++++++++++++++++++++++
1 files changed, 37 insertions(+), 0 deletions(-)

diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index 40a8a9d..61f2781 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -258,6 +258,40 @@ static int a2mp_createphyslink_req(struct amp_mgr *mgr, struct sk_buff *skb,
return 0;
}

+static int a2mp_discphyslink_req(struct amp_mgr *mgr, struct sk_buff *skb,
+ struct a2mp_cmd *hdr)
+{
+ struct a2mp_physlink_req *req = (struct a2mp_physlink_req *)skb->data;
+ struct a2mp_physlink_rsp rsp;
+ struct hci_dev *hdev;
+
+ if (le16_to_cpu(hdr->len) < sizeof(*req))
+ return -EINVAL;
+
+ rsp.local_id = req->remote_id;
+ rsp.remote_id = req->local_id;
+ rsp.status = A2MP_STATUS_SUCCESS;
+
+ BT_DBG("local_id %d remote_id %d", rsp.local_id, rsp.remote_id);
+
+ hdev = hci_dev_get(req->local_id);
+ if (!hdev) {
+ rsp.status = A2MP_STATUS_INVALID_CTRL_ID;
+ goto send_rsp;
+ }
+
+ /* TODO Disconnect Phys Link here */
+
+send_rsp:
+ a2mp_send(mgr, A2MP_DISCONNPHYSLINK_RSP, hdr->ident, sizeof(rsp), &rsp);
+
+ if (hdev)
+ hci_dev_put(hdev);
+
+ skb_pull(skb, sizeof(*req));
+ return 0;
+}
+
/* Handle A2MP signalling */
static int a2mp_chan_recv_cb(void *data, struct sk_buff *skb)
{
@@ -308,6 +342,9 @@ static int a2mp_chan_recv_cb(void *data, struct sk_buff *skb)
break;

case A2MP_DISCONNPHYSLINK_REQ:
+ err = a2mp_discphyslink_req(mgr, skb, hdr);
+ break;
+
case A2MP_CHANGE_RSP:
case A2MP_DISCOVER_RSP:
case A2MP_GETINFO_RSP:
--
1.7.9.1


2012-03-15 12:30:07

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFCv4 16/30] Bluetooth: A2MP: Process A2MP Command Reject

From: Andrei Emeltchenko <[email protected]>


Signed-off-by: Andrei Emeltchenko <[email protected]>
---
net/bluetooth/a2mp.c | 19 +++++++++++++++++++
1 files changed, 19 insertions(+), 0 deletions(-)

diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index b9b14e5..19c3422 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -64,6 +64,22 @@ static void a2mp_send(struct amp_mgr *mgr, u8 code, u8 ident, u16 len,
kfree(cmd);
}

+/* Processing A2MP messages */
+static inline int a2mp_command_rej(struct amp_mgr *mgr, struct sk_buff *skb,
+ struct a2mp_cmd *hdr)
+{
+ struct a2mp_cmd_rej *rej = (struct a2mp_cmd_rej *)skb->data;
+
+ if (le16_to_cpu(hdr->len) < sizeof(*rej))
+ return -EINVAL;
+
+ BT_DBG("ident %d reason %d", hdr->ident, le16_to_cpu(rej->reason));
+
+ skb_pull(skb, sizeof(*rej));
+
+ return 0;
+}
+
/* Handle A2MP signalling */
static int a2mp_chan_recv_cb(void *data, struct sk_buff *skb)
{
@@ -90,6 +106,9 @@ static int a2mp_chan_recv_cb(void *data, struct sk_buff *skb)

switch (hdr->code) {
case A2MP_COMMAND_REJ:
+ a2mp_command_rej(mgr, skb, hdr);
+ break;
+
case A2MP_DISCOVER_REQ:
case A2MP_CHANGE_NOTIFY:
case A2MP_GETINFO_REQ:
--
1.7.9.1


2012-03-15 12:30:13

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFCv4 22/30] Bluetooth: A2MP: Process A2MP Create Physlink Request

From: Andrei Emeltchenko <[email protected]>


Signed-off-by: Andrei Emeltchenko <[email protected]>
---
net/bluetooth/a2mp.c | 19 +++++++++++++++++++
1 files changed, 19 insertions(+), 0 deletions(-)

diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index a275879..40a8a9d 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -242,6 +242,22 @@ static int a2mp_getampassoc_req(struct amp_mgr *mgr, struct sk_buff *skb,
return 0;
}

+static int a2mp_createphyslink_req(struct amp_mgr *mgr, struct sk_buff *skb,
+ struct a2mp_cmd *hdr)
+{
+ struct a2mp_physlink_req *req = (struct a2mp_physlink_req *)skb->data;
+
+ if (le16_to_cpu(hdr->len) < sizeof(*req))
+ return -EINVAL;
+
+ BT_DBG("local_id %d, remote_id %d", req->local_id, req->remote_id);
+
+ /* TODO process physlink create */
+
+ skb_pull(skb, __le16_to_cpu(hdr->len));
+ return 0;
+}
+
/* Handle A2MP signalling */
static int a2mp_chan_recv_cb(void *data, struct sk_buff *skb)
{
@@ -288,6 +304,9 @@ static int a2mp_chan_recv_cb(void *data, struct sk_buff *skb)
break;

case A2MP_CREATEPHYSLINK_REQ:
+ err = a2mp_createphyslink_req(mgr, skb, hdr);
+ break;
+
case A2MP_DISCONNPHYSLINK_REQ:
case A2MP_CHANGE_RSP:
case A2MP_DISCOVER_RSP:
--
1.7.9.1


2012-03-15 12:30:12

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFCv4 21/30] Bluetooth: A2MP: Process A2MP Get AMP Assoc Request

From: Andrei Emeltchenko <[email protected]>

Example trace when receiving AMP Assoc Request with wrong AMP id.
...
> ACL data: handle 11 flags 0x02 dlen 13
A2MP: Get AMP Assoc req: id 238
< ACL data: handle 11 flags 0x00 dlen 14
A2MP: Get AMP Assoc rsp: id 238 status (1) Invalid Controller ID
assoc data:
...

Signed-off-by: Andrei Emeltchenko <[email protected]>
---
net/bluetooth/a2mp.c | 32 ++++++++++++++++++++++++++++++++
1 files changed, 32 insertions(+), 0 deletions(-)

diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index 96d4dfe..a275879 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -213,6 +213,35 @@ static inline int a2mp_getinfo_req(struct amp_mgr *mgr, struct sk_buff *skb,
return 0;
}

+static int a2mp_getampassoc_req(struct amp_mgr *mgr, struct sk_buff *skb,
+ struct a2mp_cmd *hdr)
+{
+ struct a2mp_amp_assoc_req *req =
+ (struct a2mp_amp_assoc_req *)skb->data;
+ struct hci_dev *hdev;
+
+ if (le16_to_cpu(hdr->len) < sizeof(*req))
+ return -EINVAL;
+
+ BT_DBG("id %d", req->id);
+
+ hdev = hci_dev_get(req->id);
+ if (!hdev || hdev->amp_type == HCI_BREDR) {
+ struct a2mp_amp_assoc_rsp rsp;
+ rsp.id = req->id;
+ rsp.status = A2MP_STATUS_INVALID_CTRL_ID;
+
+ a2mp_send(mgr, A2MP_GETAMPASSOC_RSP, hdr->ident, sizeof(rsp),
+ &rsp);
+ }
+
+ if (hdev)
+ hci_dev_put(hdev);
+
+ skb_pull(skb, sizeof(*req));
+ return 0;
+}
+
/* Handle A2MP signalling */
static int a2mp_chan_recv_cb(void *data, struct sk_buff *skb)
{
@@ -255,6 +284,9 @@ static int a2mp_chan_recv_cb(void *data, struct sk_buff *skb)
break;

case A2MP_GETAMPASSOC_REQ:
+ err = a2mp_getampassoc_req(mgr, skb, hdr);
+ break;
+
case A2MP_CREATEPHYSLINK_REQ:
case A2MP_DISCONNPHYSLINK_REQ:
case A2MP_CHANGE_RSP:
--
1.7.9.1


2012-03-15 12:30:15

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFCv4 24/30] Bluetooth: A2MP: Process A2MP Command Responses

From: Andrei Emeltchenko <[email protected]>


Signed-off-by: Andrei Emeltchenko <[email protected]>
---
net/bluetooth/a2mp.c | 12 ++++++++++++
1 files changed, 12 insertions(+), 0 deletions(-)

diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index 61f2781..0ef69de 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -292,6 +292,15 @@ send_rsp:
return 0;
}

+static inline int a2mp_cmd_rsp(struct amp_mgr *mgr, struct sk_buff *skb,
+ struct a2mp_cmd *hdr)
+{
+ BT_DBG("ident %d code %d", hdr->ident, hdr->code);
+
+ skb_pull(skb, __le16_to_cpu(hdr->len));
+ return 0;
+}
+
/* Handle A2MP signalling */
static int a2mp_chan_recv_cb(void *data, struct sk_buff *skb)
{
@@ -351,6 +360,9 @@ static int a2mp_chan_recv_cb(void *data, struct sk_buff *skb)
case A2MP_GETAMPASSOC_RSP:
case A2MP_CREATEPHYSLINK_RSP:
case A2MP_DISCONNPHYSLINK_RSP:
+ err = a2mp_cmd_rsp(mgr, skb, hdr);
+ break;
+
default:
BT_ERR("Unknown A2MP sig cmd 0x%2.2x", hdr->code);
err = -EINVAL;
--
1.7.9.1


2012-03-15 12:30:08

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFCv4 17/30] Bluetooth: A2MP: Helper functions to count HCI devs

From: Andrei Emeltchenko <[email protected]>

Helper functions used to cound HCI devices (AMP controllers) and
build controller list packet.

Signed-off-by: Andrei Emeltchenko <[email protected]>
---
include/net/bluetooth/hci.h | 2 ++
include/net/bluetooth/hci_core.h | 13 +++++++++++++
net/bluetooth/a2mp.c | 31 +++++++++++++++++++++++++++++++
3 files changed, 46 insertions(+), 0 deletions(-)

diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index 344b0f9..852e9e2 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -56,6 +56,8 @@
#define HCI_BREDR 0x00
#define HCI_AMP 0x01

+#define HCI_BREDR_ID 0
+
/* HCI device quirks */
enum {
HCI_QUIRK_NO_RESET,
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index 828fc7b..18433d0 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -639,6 +639,19 @@ static inline void hci_set_drvdata(struct hci_dev *hdev, void *data)
dev_set_drvdata(&hdev->dev, data);
}

+/* hci_dev_list shall be locked */
+static inline uint8_t __hci_num_ctrl(void)
+{
+ uint8_t count = 0;
+ struct list_head *p;
+
+ list_for_each(p, &hci_dev_list) {
+ count++;
+ }
+
+ return count;
+}
+
struct hci_dev *hci_dev_get(int index);
struct hci_dev *hci_get_route(bdaddr_t *src, bdaddr_t *dst);

diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index 19c3422..ac5084c 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -13,6 +13,7 @@
*/

#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
#include <net/bluetooth/l2cap.h>
#include <net/bluetooth/a2mp.h>

@@ -64,6 +65,36 @@ static void a2mp_send(struct amp_mgr *mgr, u8 code, u8 ident, u16 len,
kfree(cmd);
}

+static inline void __a2mp_cl_bredr(struct a2mp_cl *cl)
+{
+ cl->id = 0;
+ cl->type = 0;
+ cl->status = 1;
+}
+
+/* hci_dev_list shall be locked */
+static void __a2mp_add_cl(struct amp_mgr *mgr, struct a2mp_cl *cl, u8 num_ctrl)
+{
+ int i = 0;
+ struct hci_dev *hdev;
+
+ __a2mp_cl_bredr(cl);
+
+ list_for_each_entry(hdev, &hci_dev_list, list) {
+ /* Iterate through AMP controllers */
+ if (hdev->id == HCI_BREDR_ID)
+ continue;
+
+ /* Starting from second entry */
+ if (++i >= num_ctrl)
+ return;
+
+ cl[i].id = hdev->id;
+ cl[i].type = hdev->amp_type;
+ cl[i].status = hdev->amp_status;
+ }
+}
+
/* Processing A2MP messages */
static inline int a2mp_command_rej(struct amp_mgr *mgr, struct sk_buff *skb,
struct a2mp_cmd *hdr)
--
1.7.9.1


2012-03-15 12:30:16

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFCv4 25/30] Bluetooth: A2MP: Handling fixed channels

From: Andrei Emeltchenko <[email protected]>

A2MP fixed channel do not have sk

Signed-off-by: Andrei Emeltchenko <[email protected]>
---
include/net/bluetooth/l2cap.h | 1 +
net/bluetooth/a2mp.c | 4 ++--
net/bluetooth/l2cap_core.c | 15 +++++++++++++--
3 files changed, 16 insertions(+), 4 deletions(-)

diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
index b3f9854..15c0f03 100644
--- a/include/net/bluetooth/l2cap.h
+++ b/include/net/bluetooth/l2cap.h
@@ -557,6 +557,7 @@ struct l2cap_conn {
#define L2CAP_CHAN_RAW 1
#define L2CAP_CHAN_CONN_LESS 2
#define L2CAP_CHAN_CONN_ORIENTED 3
+#define L2CAP_CHAN_CONN_FIX_A2MP 4

/* ----- L2CAP socket info ----- */
#define l2cap_pi(sk) ((struct l2cap_pinfo *) sk)
diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index 0ef69de..7709fcd 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -433,9 +433,9 @@ static struct l2cap_chan *open_a2mp_chan(struct l2cap_conn *conn)

BT_DBG("chan %p", chan);

+ chan->chan_type = L2CAP_CHAN_CONN_FIX_A2MP;
+
chan->sec_level = BT_SECURITY_LOW;
- chan->omtu = L2CAP_A2MP_DEFAULT_MTU;
- chan->imtu = L2CAP_A2MP_DEFAULT_MTU;
chan->flush_to = L2CAP_DEFAULT_FLUSH_TO;
chan->fcs = L2CAP_FCS_CRC16;

diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index e8855cc..d840aae 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -333,6 +333,13 @@ static void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
chan->omtu = L2CAP_DEFAULT_MTU;
break;

+ case L2CAP_CHAN_CONN_FIX_A2MP:
+ chan->scid = L2CAP_CID_A2MP;
+ chan->dcid = L2CAP_CID_A2MP;
+ chan->omtu = L2CAP_A2MP_DEFAULT_MTU;
+ chan->imtu = L2CAP_A2MP_DEFAULT_MTU;
+ break;
+
default:
/* Raw socket can send/recv signalling messages only */
chan->scid = L2CAP_CID_SIGNALING;
@@ -363,7 +370,7 @@ static void l2cap_chan_del(struct l2cap_chan *chan, int err)
{
struct sock *sk = chan->sk;
struct l2cap_conn *conn = chan->conn;
- struct sock *parent = bt_sk(sk)->parent;
+ struct sock *parent;

__clear_chan_timer(chan);

@@ -379,12 +386,15 @@ static void l2cap_chan_del(struct l2cap_chan *chan, int err)
hci_conn_put(conn->hcon);
}

+ if (chan->chan_type == L2CAP_CHAN_CONN_FIX_A2MP)
+ goto clean;
+
lock_sock(sk);

__l2cap_state_change(chan, BT_CLOSED, err);
-
sock_set_flag(sk, SOCK_ZAPPED);

+ parent = bt_sk(sk)->parent;
if (parent) {
bt_accept_unlink(sk);
parent->sk_data_ready(parent, 0);
@@ -397,6 +407,7 @@ static void l2cap_chan_del(struct l2cap_chan *chan, int err)
test_bit(CONF_INPUT_DONE, &chan->conf_state)))
return;

+clean:
skb_queue_purge(&chan->tx_q);

if (chan->mode == L2CAP_MODE_ERTM) {
--
1.7.9.1


2012-03-15 12:30:03

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFCv4 12/30] Bluetooth: A2MP: skb allocation callback

From: Andrei Emeltchenko <[email protected]>


Signed-off-by: Andrei Emeltchenko <[email protected]>
---
net/bluetooth/a2mp.c | 7 +++++++
1 files changed, 7 insertions(+), 0 deletions(-)

diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index 19a8ac3..1607592 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -89,11 +89,18 @@ static void a2mp_chan_close_cb(void *data)
l2cap_chan_destroy(mgr->a2mp_chan);
}

+static struct sk_buff *a2mp_chan_alloc_skb_cb(struct l2cap_chan *chan,
+ unsigned long len, int nb, int *err)
+{
+ return bt_skb_alloc(len, GFP_KERNEL);
+}
+
static struct l2cap_ops a2mp_chan_ops = {
.name = "L2CAP A2MP channel",
.close = a2mp_chan_close_cb,
.send = l2cap_chan_send,
.state_change = a2mp_chan_state_change_cb,
+ .alloc_skb = a2mp_chan_alloc_skb_cb,
};

static struct l2cap_chan *open_a2mp_chan(struct l2cap_conn *conn)
--
1.7.9.1


2012-03-15 12:30:04

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFCv4 13/30] Bluetooth: A2MP: Definitions for A2MP commands

From: Andrei Emeltchenko <[email protected]>

Define A2MP command id and packet structures.

Signed-off-by: Andrei Emeltchenko <[email protected]>
---
include/net/bluetooth/a2mp.h | 72 ++++++++++++++++++++++++++++++++++++++++++
1 files changed, 72 insertions(+), 0 deletions(-)

diff --git a/include/net/bluetooth/a2mp.h b/include/net/bluetooth/a2mp.h
index 995f1c0..6e6202a 100644
--- a/include/net/bluetooth/a2mp.h
+++ b/include/net/bluetooth/a2mp.h
@@ -31,6 +31,78 @@ struct a2mp_cmd {
__u8 data[0];
} __packed;

+/* A2MP command codes */
+#define A2MP_COMMAND_REJ 0x01
+struct a2mp_cmd_rej {
+ __le16 reason;
+} __packed;
+
+#define A2MP_DISCOVER_REQ 0x02
+struct a2mp_discov_req {
+ __le16 mtu;
+ __le16 ext_feat;
+} __packed;
+
+struct a2mp_cl {
+ __u8 id;
+ __u8 type;
+ __u8 status;
+} __packed;
+
+#define A2MP_DISCOVER_RSP 0x03
+struct a2mp_discov_rsp {
+ __le16 mtu;
+ __le16 ext_feat;
+ struct a2mp_cl cl[0];
+} __packed;
+
+#define A2MP_CHANGE_NOTIFY 0x04
+#define A2MP_CHANGE_RSP 0x05
+
+#define A2MP_GETINFO_REQ 0x06
+struct a2mp_info_req {
+ __u8 id;
+} __packed;
+
+#define A2MP_GETINFO_RSP 0x07
+struct a2mp_info_rsp {
+ __u8 id;
+ __u8 status;
+ __le32 total_bw;
+ __le32 max_bw;
+ __le32 min_latency;
+ __le16 pal_cap;
+ __le16 assoc_size;
+} __packed;
+
+#define A2MP_GETAMPASSOC_REQ 0x08
+struct a2mp_amp_assoc_req {
+ __u8 id;
+} __packed;
+
+#define A2MP_GETAMPASSOC_RSP 0x09
+struct a2mp_amp_assoc_rsp {
+ __u8 id;
+ __u8 status;
+ __u8 amp_assoc[0];
+} __packed;
+
+#define A2MP_CREATEPHYSLINK_REQ 0x0A
+#define A2MP_DISCONNPHYSLINK_REQ 0x0C
+struct a2mp_physlink_req {
+ __u8 local_id;
+ __u8 remote_id;
+ __u8 amp_assoc[0];
+} __packed;
+
+#define A2MP_CREATEPHYSLINK_RSP 0x0B
+#define A2MP_DISCONNPHYSLINK_RSP 0x0D
+struct a2mp_physlink_rsp {
+ __u8 local_id;
+ __u8 remote_id;
+ __u8 status;
+} __packed;
+
void amp_mgr_get(struct amp_mgr *mgr);
int amp_mgr_put(struct amp_mgr *mgr);

--
1.7.9.1


2012-03-15 12:30:05

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFCv4 14/30] Bluetooth: A2MP: Define A2MP status codes

From: Andrei Emeltchenko <[email protected]>

A2MP status codes copied from Bluez patch sent by Peter Krystad
<[email protected]>.

Signed-off-by: Andrei Emeltchenko <[email protected]>
---
include/net/bluetooth/a2mp.h | 10 ++++++++++
1 files changed, 10 insertions(+), 0 deletions(-)

diff --git a/include/net/bluetooth/a2mp.h b/include/net/bluetooth/a2mp.h
index 6e6202a..c99c375 100644
--- a/include/net/bluetooth/a2mp.h
+++ b/include/net/bluetooth/a2mp.h
@@ -103,6 +103,16 @@ struct a2mp_physlink_rsp {
__u8 status;
} __packed;

+/* A2MP response status */
+#define A2MP_STATUS_SUCCESS 0x00
+#define A2MP_STATUS_INVALID_CTRL_ID 0x01
+#define A2MP_STATUS_UNABLE_START_LINK_CREATION 0x02
+#define A2MP_STATUS_NO_PHYSICAL_LINK_EXISTS 0x02
+#define A2MP_STATUS_COLLISION_OCCURED 0x03
+#define A2MP_STATUS_DISCONN_REQ_RECVD 0x04
+#define A2MP_STATUS_PHYS_LINK_EXISTS 0x05
+#define A2MP_STATUS_SECURITY_VIOLATION 0x06
+
void amp_mgr_get(struct amp_mgr *mgr);
int amp_mgr_put(struct amp_mgr *mgr);

--
1.7.9.1


2012-03-15 12:30:00

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFCv4 09/30] Bluetooth: A2MP: Add channel close callback

From: Andrei Emeltchenko <[email protected]>


Signed-off-by: Andrei Emeltchenko <[email protected]>
---
net/bluetooth/a2mp.c | 8 ++++++++
1 files changed, 8 insertions(+), 0 deletions(-)

diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index 66edcfa..5945788 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -16,8 +16,16 @@
#include <net/bluetooth/l2cap.h>
#include <net/bluetooth/a2mp.h>

+static void a2mp_chan_close_cb(void *data)
+{
+ struct amp_mgr *mgr = data;
+
+ l2cap_chan_destroy(mgr->a2mp_chan);
+}
+
static struct l2cap_ops a2mp_chan_ops = {
.name = "L2CAP A2MP channel",
+ .close = a2mp_chan_close_cb,
};

static struct l2cap_chan *open_a2mp_chan(struct l2cap_conn *conn)
--
1.7.9.1


2012-03-15 12:29:56

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFCv4 05/30] Bluetooth: Lock sk only if exist

From: Andrei Emeltchenko <[email protected]>


Signed-off-by: Andrei Emeltchenko <[email protected]>
---
net/bluetooth/l2cap_core.c | 8 ++++++--
1 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 779f819..e8855cc 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -212,9 +212,13 @@ static inline void l2cap_state_change(struct l2cap_chan *chan, int state, int er
{
struct sock *sk = chan->sk;

- lock_sock(sk);
+ if (sk)
+ lock_sock(sk);
+
__l2cap_state_change(chan, state, err);
- release_sock(sk);
+
+ if (sk)
+ release_sock(sk);
}

void __l2cap_chan_set_err(struct l2cap_chan *chan, int err)
--
1.7.9.1


2012-03-15 12:29:52

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFCv4 01/30] Bluetooth: Make ertm_init available

From: Andrei Emeltchenko <[email protected]>

Function l2cap_ertm_init will be used for initialization
fixed A2MP channel.

Signed-off-by: Andrei Emeltchenko <[email protected]>
---
include/net/bluetooth/l2cap.h | 1 +
net/bluetooth/l2cap_core.c | 2 +-
2 files changed, 2 insertions(+), 1 deletions(-)

diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
index 9b242c6..c107944 100644
--- a/include/net/bluetooth/l2cap.h
+++ b/include/net/bluetooth/l2cap.h
@@ -861,5 +861,6 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,
u32 priority);
void l2cap_chan_busy(struct l2cap_chan *chan, int busy);
int l2cap_chan_check_security(struct l2cap_chan *chan);
+void l2cap_ertm_init(struct l2cap_chan *chan);

#endif /* __L2CAP_H */
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 0a46603..b0d1cee 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -2038,7 +2038,7 @@ static void l2cap_ack_timeout(struct work_struct *work)
l2cap_chan_put(chan);
}

-static inline void l2cap_ertm_init(struct l2cap_chan *chan)
+void l2cap_ertm_init(struct l2cap_chan *chan)
{
chan->expected_ack_seq = 0;
chan->unacked_frames = 0;
--
1.7.9.1


2012-03-15 12:29:53

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFCv4 02/30] Bluetooth: Add send function to chan ops

From: Andrei Emeltchenko <[email protected]>


Signed-off-by: Andrei Emeltchenko <[email protected]>
---
include/net/bluetooth/l2cap.h | 2 ++
1 files changed, 2 insertions(+), 0 deletions(-)

diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
index c107944..260f93e 100644
--- a/include/net/bluetooth/l2cap.h
+++ b/include/net/bluetooth/l2cap.h
@@ -505,6 +505,8 @@ struct l2cap_ops {

struct l2cap_chan *(*new_connection) (void *data);
int (*recv) (void *data, struct sk_buff *skb);
+ int (*send) (struct l2cap_chan *chan,
+ struct msghdr *msg, size_t len, u32 priority);
void (*close) (void *data);
void (*state_change) (void *data, int state);
struct sk_buff *(*alloc_skb) (struct l2cap_chan *chan,
--
1.7.9.1