Linux bluetooth development
 help / color / mirror / Atom feed
* Re: [PATCH 1/3] Add BT3 AMP device support, by Atheros Linux BT3 team.
From: Gustavo F. Padovan @ 2010-07-15 19:42 UTC (permalink / raw)
  To: Luis R. Rodriguez
  Cc: Dan Tian, linux-bluetooth@vger.kernel.org, Haijun Liu,
	Luis Rodriguez
In-Reply-To: <20100715184308.GF1166@tux>

Hi Luis,

* Luis R. Rodriguez <lrodriguez@atheros.com> [2010-07-15 11:43:08 -0700]:

> On Thu, Jul 15, 2010 at 08:09:21AM -0700, Gustavo F. Padovan wrote:
> > Hi Dan,
> > 
> > * Dan Tian <Dan.Tian@Atheros.com> [2010-07-15 15:37:32 +0800]:
> > 
> > > From 40cbfbf5c1bc9109364970956d9b6f74f5846c70 Mon Sep 17 00:00:00 2001
> > > From: Haijun.Liu <Haijun.Liu@Atheros.com>
> > > Date: Wed, 14 Jul 2010 22:50:56 +0800
> > > Subject: [PATCH 1/3] Add BT3 AMP device support, by Atheros Linux BT3 team.
> > > 
> > > 
> > > Signed-off-by: Haijun.Liu <Haijun.Liu@Atheros.com>
> > > ---
> > >  include/net/bluetooth/hci.h      |  342 +++++++++++++++++++-
> > >  include/net/bluetooth/hci_core.h |  264 ++++++++++++++-
> > >  net/bluetooth/cmtp/core.c        |    1 +
> > >  net/bluetooth/hci_conn.c         |  201 +++++++++++-
> > >  net/bluetooth/hci_core.c         |  349 ++++++++++++++++++--
> > >  net/bluetooth/hci_event.c        |  698 +++++++++++++++++++++++++++++++++++++-
> > >  6 files changed, 1822 insertions(+), 33 deletions(-)
> > 
> > 
> > Could you split all your patches in little chunks? +1882 is very hard to
> > review. And please be more verbose on the commit message, it can help us
> > figure out what's happening.
> 
> This used to be 1 large patch, it was recently split up to 3, I havent't
> had a chance yet to review further but my understanding is this this was
> split up as much as possible in the last try. If you can provide suggestions
> how to split it up more that might be useful, but agreed, if possible more
> splitup would help.

But it is still too big. I looked to patch 3/3, it does a lot different
things in the same patch that make the patch impossible to be tracked.
We need atomic patches for at least each change in the ERTM code, each
new state added in the L2CAP state machine, each new HS feature, etc,
i.e., we need proper patches with proper commit messages explaining
what's happening. The same should apply to 1/3 and 2/3, I didn't looked
to them.

> 
> Dan, can you also please drop the "by Atheros Linux BT3 team." on the subject
> of the patches. This is already implied by the From and the Signed-off-by.

Dan, your patches don't apply cleanly upstream, please rebase them
against Marcel's bluetooth-next tree and do the proper split of the
patches.

-- 
Gustavo F. Padovan
http://padovan.org

^ permalink raw reply

* Re: [PATCH 3/3] Add eL2cap implementation, by Atheros Linux BT3 team.
From: Luis R. Rodriguez @ 2010-07-15 18:51 UTC (permalink / raw)
  To: Dan Tian; +Cc: linux-bluetooth@vger.kernel.org, Haijun Liu, Luis Rodriguez
In-Reply-To: <CDAFEDABF718A54BABD0DA5476695307CE881F90@SHEXMB-01.global.atheros.com>

On Thu, Jul 15, 2010 at 04:59:45AM -0700, Dan Tian wrote:
> The third patch, which is too big to send via pure text in email.
> 
> Please see attached.

I'm not sure if MIME attachments are accepted to linux-bluetooth so
I've scp'd the patch you attached and that I did get to kernel.org
for others to wget:

http://kernel.org/pub/linux/kernel/people/mcgrof/patches/bluetooth/BT-3.0/

  Luis

^ permalink raw reply

* Re: [PATCH 1/3] Add BT3 AMP device support, by Atheros Linux BT3 team.
From: Luis R. Rodriguez @ 2010-07-15 18:43 UTC (permalink / raw)
  To: Gustavo F. Padovan
  Cc: Dan Tian, linux-bluetooth@vger.kernel.org, Haijun Liu,
	Luis Rodriguez
In-Reply-To: <20100715150921.GA3188@vigoh>

On Thu, Jul 15, 2010 at 08:09:21AM -0700, Gustavo F. Padovan wrote:
> Hi Dan,
> 
> * Dan Tian <Dan.Tian@Atheros.com> [2010-07-15 15:37:32 +0800]:
> 
> > From 40cbfbf5c1bc9109364970956d9b6f74f5846c70 Mon Sep 17 00:00:00 2001
> > From: Haijun.Liu <Haijun.Liu@Atheros.com>
> > Date: Wed, 14 Jul 2010 22:50:56 +0800
> > Subject: [PATCH 1/3] Add BT3 AMP device support, by Atheros Linux BT3 team.
> > 
> > 
> > Signed-off-by: Haijun.Liu <Haijun.Liu@Atheros.com>
> > ---
> >  include/net/bluetooth/hci.h      |  342 +++++++++++++++++++-
> >  include/net/bluetooth/hci_core.h |  264 ++++++++++++++-
> >  net/bluetooth/cmtp/core.c        |    1 +
> >  net/bluetooth/hci_conn.c         |  201 +++++++++++-
> >  net/bluetooth/hci_core.c         |  349 ++++++++++++++++++--
> >  net/bluetooth/hci_event.c        |  698 +++++++++++++++++++++++++++++++++++++-
> >  6 files changed, 1822 insertions(+), 33 deletions(-)
> 
> 
> Could you split all your patches in little chunks? +1882 is very hard to
> review. And please be more verbose on the commit message, it can help us
> figure out what's happening.

This used to be 1 large patch, it was recently split up to 3, I havent't
had a chance yet to review further but my understanding is this this was
split up as much as possible in the last try. If you can provide suggestions
how to split it up more that might be useful, but agreed, if possible more
splitup would help.

Dan, can you also please drop the "by Atheros Linux BT3 team." on the subject
of the patches. This is already implied by the From and the Signed-off-by.

  Luis

^ permalink raw reply

* RE: [PATCH 3/3] Bluetooth: Synchronize SCO/eSCO connection requests to ACL state
From: Perelet, Oleg @ 2010-07-15 17:23 UTC (permalink / raw)
  To: Marcel Holtmann; +Cc: Matt Wilson, Ron Shaffer, linux-bluetooth@vger.kernel.org
In-Reply-To: <1279174619.6282.63.camel@localhost.localdomain>

>Also until the SCO setup is completed the SCO socket should
>never be connected. So userspace can properly detect if SCO setup is in
>progress and route it to the speaker with a proper audio policy. So I
>think the emergency call argument is void. 

Way it works (most of opensource phone systems) - when phone is "connected" to HS/CarKit with no active Ccall ACL + RFCOMM (for AT commnds) is up, but SCO is down for power savings because there's no active audio. Physically audio is routed to HS all the time while HS ACL is up.

When you enter call state phone will attempt to establish SCO over existing ACL with audio still routed to BT - this is when things get screwed up. 

several seconds later unsniff will fail, ACL will get messed up and disconnected and phone will switch to speaker.

>So userspace can properly detect if SCO setup is in
>progress and route it to the speaker with a proper audio policy.

So during "normal" SCO setup (which can take up to 2.5 secs) you suggest to route audio back to speaker for 2.5 secs and then put it back to BT?:)


Oleg.

^ permalink raw reply

* Re: [PATCH 1/3] Add BT3 AMP device support, by Atheros Linux BT3 team.
From: Gustavo F. Padovan @ 2010-07-15 15:09 UTC (permalink / raw)
  To: Dan Tian; +Cc: linux-bluetooth@vger.kernel.org, Haijun Liu, Luis Rodriguez
In-Reply-To: <CDAFEDABF718A54BABD0DA5476695307CE881EC1@SHEXMB-01.global.atheros.com>

Hi Dan,

* Dan Tian <Dan.Tian@Atheros.com> [2010-07-15 15:37:32 +0800]:

> From 40cbfbf5c1bc9109364970956d9b6f74f5846c70 Mon Sep 17 00:00:00 2001
> From: Haijun.Liu <Haijun.Liu@Atheros.com>
> Date: Wed, 14 Jul 2010 22:50:56 +0800
> Subject: [PATCH 1/3] Add BT3 AMP device support, by Atheros Linux BT3 team.
> 
> 
> Signed-off-by: Haijun.Liu <Haijun.Liu@Atheros.com>
> ---
>  include/net/bluetooth/hci.h      |  342 +++++++++++++++++++-
>  include/net/bluetooth/hci_core.h |  264 ++++++++++++++-
>  net/bluetooth/cmtp/core.c        |    1 +
>  net/bluetooth/hci_conn.c         |  201 +++++++++++-
>  net/bluetooth/hci_core.c         |  349 ++++++++++++++++++--
>  net/bluetooth/hci_event.c        |  698 +++++++++++++++++++++++++++++++++++++-
>  6 files changed, 1822 insertions(+), 33 deletions(-)


Could you split all your patches in little chunks? +1882 is very hard to
review. And please be more verbose on the commit message, it can help us
figure out what's happening.


-- 
Gustavo F. Padovan
http://padovan.org

^ permalink raw reply

* Re: [PATCH 1/3] Add BT3 AMP device support, by Atheros Linux BT3 team.
From: David Vrabel @ 2010-07-15 14:29 UTC (permalink / raw)
  To: Dan Tian; +Cc: linux-bluetooth@vger.kernel.org, Haijun Liu, Luis Rodriguez
In-Reply-To: <CDAFEDABF718A54BABD0DA5476695307CE881EC1@SHEXMB-01.global.atheros.com>

Dan Tian wrote:
> From 40cbfbf5c1bc9109364970956d9b6f74f5846c70 Mon Sep 17 00:00:00 2001
> From: Haijun.Liu <Haijun.Liu@Atheros.com>
> Date: Wed, 14 Jul 2010 22:50:56 +0800
> Subject: [PATCH 1/3] Add BT3 AMP device support, by Atheros Linux BT3 team.

What happened to patch 3/3?

David
-- 
David Vrabel, Senior Software Engineer, Drivers
CSR, Churchill House, Cambridge Business Park,  Tel: +44 (0)1223 692562
Cowley Road, Cambridge, CB4 0WZ                 http://www.csr.com/


Member of the CSR plc group of companies. CSR plc registered in England and Wales, registered number 4187346, registered office Churchill House, Cambridge Business Park, Cowley Road, Cambridge, CB4 0WZ, United Kingdom

^ permalink raw reply

* blueZ library added to upstream-tracker (linuxtesting.org)
From: Andrey Ponomarenko @ 2010-07-15 12:51 UTC (permalink / raw)
  To: linux-bluetooth

Hello,

The blueZ library has been recently added to the Upstream Tracker system:
http://linuxtesting.org/upstream-tracker/versions/bluez.html

The system regularly checks for new releases, performs backward ABI
compatibility analysis and generates "shallow"-quality runtime tests.

Any bugs/feature requests or suggestions for inclusion of some library
to the tracker are very welcome. Thanks.


-- 
Andrey Ponomarenko

Linux Verification Center, ISPRAS
 web:    http://www.linuxtesting.org
 mail:   upstream-tracker@linuxtesting.org


^ permalink raw reply

* Re: Question about obexd
From: Luiz Augusto von Dentz @ 2010-07-15 12:38 UTC (permalink / raw)
  To: Rafał Michalski; +Cc: linux-bluetooth
In-Reply-To: <AANLkTimq_OEIplkLijA2TZLPz4tKOU7b4V-IYR_om_Px@mail.gmail.com>

Hi,

2010/7/15 Rafał Michalski <michalski.raf@gmail.com>:
> Hello
>
> I'd like to ask which functions in obexd daemon are involved in
> exporting: outgoing calls list, incoming calls list, missed calls list
> -  from phone to paired device (headset or carkit), and how it
> generally works ?
> I would be graceful for any help.

PBAP related driver depend on the platform, so lets say the platform
has tracker backend then you should configure --with-phonebook=tracker
and then phonebook-tracker.c will be compiled. As for the interface
this drivers should implement the functions in phonebook.h.

Does this answer your question?

-- 
Luiz Augusto von Dentz
Computer Engineer

^ permalink raw reply

* Re: SSP Link key storing issue
From: Prabhakaran Chandrasekara M @ 2010-07-15 12:36 UTC (permalink / raw)
  To: Par-Gunnar HJALMDAHL; +Cc: Waldemar.Rymarkiewicz, luiz.dentz, linux-bluetooth
In-Reply-To: <AFCDDB4A3EA003429EEF1E7B211FDBBA305D521EBE@EXDCVYMBSTM005.EQ1STM.local>

Hi Waldek,



On Thu, Jul 15, 2010 at 5:53 PM, Par-Gunnar HJALMDAHL
<par-gunnar.p.hjalmdahl@stericsson.com> wrote:
> To clarify myself below:
> Next connection would then fail anyway. ->
> Next authenticated connection would then require a new pairing procedure anyway so there is no point in saving the link key.
>
> /P-G Hjalmdahl
>
>
> -----Original Message-----
> From: Par-Gunnar HJALMDAHL
> Sent: den 15 juli 2010 14:11
> To: 'Waldemar.Rymarkiewicz@tieto.com'; luiz.dentz@gmail.com; prvb86@motorola.com
> Cc: linux-bluetooth@vger.kernel.org
> Subject: RE: SSP Link key storing issue
>
> Hi,
>
> Basically the reasoning behind all this in the specification is that there is no point in saving the link key on only one side at pairing.
> Next connection would then fail anyway.
> I agree with Waldemar, this is correct behavior by BlueZ.
>
> Best regards,
> Par-Gunnar Hjalmdahl, ST-Ericsson
>
> -----Original Message-----
> From: linux-bluetooth-owner@vger.kernel.org [mailto:linux-bluetooth-owner@vger.kernel.org] On Behalf Of Waldemar.Rymarkiewicz@tieto.com
> Sent: den 15 juli 2010 10:57
> To: luiz.dentz@gmail.com; prvb86@motorola.com
> Cc: linux-bluetooth@vger.kernel.org
> Subject: RE: SSP Link key storing issue
>
> Hi,
>
> From: linux-bluetooth-owner@vger.kernel.org [mailto:linux-bluetooth-owner@vger.kernel.org] On Behalf Of Luiz Augusto von Dentz
>>Hi,
>
>>On Thu, Jul 15, 2010 at 9:38 AM, Prabhakaran Chandrasekara M <prvb86@motorola.com> wrote:
>> Can some body please explain why the below check is considered while
>> storing link key.
>>
>>
>> On Thu, Jul 15, 2010 at 10:25 AM, Prabhakaran Chandrasekara M
>> <prvb86@motorola.com> wrote:
>>> Hello All,
>>>
>>>  I am facing some problem with SSP pairing.
>>> Sometimes Bluez does not store the Authenticated Combination link key
>>> generated during pairing process And found the below code in
>>> dbus-hci.c hci_dbus_link_key_notify
>>>
>>> /* Only store the link key if one of the following is true:
>>>      * 1. this is a legacy link key
>>>      * 2. this is a changed combination key and there was a
>>> previously
>>>      *    stored one
>>>      * 3. neither local nor remote side had no-bonding as a
>>> requirement
>>>      * 4. the local side had dedicated bonding as a requirement
>>>      * 5. the remote side is using dedicated bonding since in that
>>> case
>>>      *    also the local requirements are set to dedicated bonding
>>>      */
>
>> don;t know exactly which page, but the spec says that when one side has no-bonding, I guess 3. is about that, then the link key should not be stored. Also a2dp connection should be using medium security as we do in bluetoothd (it is the default when using BtIO) then you will got >the link key stored properly.
>
>
> In the spec 2.1 is not clearly stated, but it was explained in the errara to the GAP 2.1
> https://www.bluetooth.org/errata/errata_view.cfm?errata_id=2460
>

This errata is very clear that if a device sets auth_requirement as
"No Bonding" then it will not store the link key. Thanks for the
information.
But I am not clear that why Bluez sets Auth_requirement as "No
Bonding" during a a2dp connection. As Luiz mentioned, the Security
level is set to medium. I am analyzing the logs, I will attach the
logs in my next mail.
> You can see also spec to core 3.0  in 6.5.3.1 chapter which says

>
> "When the devices that are performing General Bonding both support Secure
> Simple Pairing, the Authentication_Requirements parameter should be set to
> MITM Protection Not Required - General Bonding unless the security policy of
> an available local service requires MITM Protection in which case the
> Authentication_Requirements parameter shall be set to MITM Protection
> Required - General Bonding. 'No bonding' is used when the device is performing
> a Secure Simple Pairing procedure, but does not intend to retain the link
> key after the physical link is disconnected."
>
> In general, this is correct behaviour of Bluez.
>
> Thanks,
> Waldek--
> To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>


-- 
Thanks,
Prabhakaran.

^ permalink raw reply

* Question about obexd
From: Rafał Michalski @ 2010-07-15 12:24 UTC (permalink / raw)
  To: linux-bluetooth

Hello

I'd like to ask which functions in obexd daemon are involved in
exporting: outgoing calls list, incoming calls list, missed calls list
-  from phone to paired device (headset or carkit), and how it
generally works ?
I would be graceful for any help.

Best Regards

Rafał Michalski, Poland

^ permalink raw reply

* RE: SSP Link key storing issue
From: Par-Gunnar HJALMDAHL @ 2010-07-15 12:23 UTC (permalink / raw)
  To: Waldemar.Rymarkiewicz@tieto.com, luiz.dentz@gmail.com,
	prvb86@motorola.com
  Cc: linux-bluetooth@vger.kernel.org
In-Reply-To: <99B09243E1A5DA4898CDD8B7001114480969964CFC@EXMB04.eu.tieto.com>

To clarify myself below:
Next connection would then fail anyway. ->
Next authenticated connection would then require a new pairing procedure anyway so there is no point in saving the link key.

/P-G Hjalmdahl


-----Original Message-----
From: Par-Gunnar HJALMDAHL 
Sent: den 15 juli 2010 14:11
To: 'Waldemar.Rymarkiewicz@tieto.com'; luiz.dentz@gmail.com; prvb86@motorola.com
Cc: linux-bluetooth@vger.kernel.org
Subject: RE: SSP Link key storing issue

Hi,

Basically the reasoning behind all this in the specification is that there is no point in saving the link key on only one side at pairing.
Next connection would then fail anyway.
I agree with Waldemar, this is correct behavior by BlueZ.

Best regards,
Par-Gunnar Hjalmdahl, ST-Ericsson

-----Original Message-----
From: linux-bluetooth-owner@vger.kernel.org [mailto:linux-bluetooth-owner@vger.kernel.org] On Behalf Of Waldemar.Rymarkiewicz@tieto.com
Sent: den 15 juli 2010 10:57
To: luiz.dentz@gmail.com; prvb86@motorola.com
Cc: linux-bluetooth@vger.kernel.org
Subject: RE: SSP Link key storing issue

Hi,

From: linux-bluetooth-owner@vger.kernel.org [mailto:linux-bluetooth-owner@vger.kernel.org] On Behalf Of Luiz Augusto von Dentz
>Hi,

>On Thu, Jul 15, 2010 at 9:38 AM, Prabhakaran Chandrasekara M <prvb86@motorola.com> wrote:
> Can some body please explain why the below check is considered while 
> storing link key.
>
>
> On Thu, Jul 15, 2010 at 10:25 AM, Prabhakaran Chandrasekara M 
> <prvb86@motorola.com> wrote:
>> Hello All,
>>
>>  I am facing some problem with SSP pairing.
>> Sometimes Bluez does not store the Authenticated Combination link key 
>> generated during pairing process And found the below code in 
>> dbus-hci.c hci_dbus_link_key_notify
>>
>> /* Only store the link key if one of the following is true:
>>      * 1. this is a legacy link key
>>      * 2. this is a changed combination key and there was a 
>> previously
>>      *    stored one
>>      * 3. neither local nor remote side had no-bonding as a 
>> requirement
>>      * 4. the local side had dedicated bonding as a requirement
>>      * 5. the remote side is using dedicated bonding since in that 
>> case
>>      *    also the local requirements are set to dedicated bonding
>>      */

> don;t know exactly which page, but the spec says that when one side has no-bonding, I guess 3. is about that, then the link key should not be stored. Also a2dp connection should be using medium security as we do in bluetoothd (it is the default when using BtIO) then you will got >the link key stored properly.


In the spec 2.1 is not clearly stated, but it was explained in the errara to the GAP 2.1
https://www.bluetooth.org/errata/errata_view.cfm?errata_id=2460

You can see also spec to core 3.0  in 6.5.3.1 chapter which says

"When the devices that are performing General Bonding both support Secure
Simple Pairing, the Authentication_Requirements parameter should be set to
MITM Protection Not Required - General Bonding unless the security policy of
an available local service requires MITM Protection in which case the
Authentication_Requirements parameter shall be set to MITM Protection
Required - General Bonding. 'No bonding' is used when the device is performing
a Secure Simple Pairing procedure, but does not intend to retain the link
key after the physical link is disconnected."

In general, this is correct behaviour of Bluez.

Thanks,
Waldek--
To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* [PATCH] bluetooth: retry reset for devices that fail
From: Oliver Neukum @ 2010-07-15 12:23 UTC (permalink / raw)
  To: Marcel Holtmann; +Cc: linux-bluetooth

=46rom af61f0663f095599003290b13ad6fadee1d4fb25 Mon Sep 17 00:00:00 2001
=46rom: Oliver Neukum <oliver@neukum.org>
Date: Tue, 6 Jul 2010 08:08:42 +0200
Subject: [PATCH] bluetooth: retry reset for devices that fail

Some devices fail to reset properly at the first attempt to reset
them under unknown circumstances. Failures can be identified by
an invalid btaddr. Retry in those cases.

Signed-off-by: Oliver Neukum <oneukum@suse.de>
=2D--
 net/bluetooth/hci_core.c |   48 ++++++++++++++++++++++++++----------------=
=2D--
 1 files changed, 28 insertions(+), 20 deletions(-)

diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index 2f768de..c47f73d 100644
=2D-- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -180,11 +180,17 @@ static void hci_reset_req(struct hci_dev *hdev, unsig=
ned long opt)
 	hci_send_cmd(hdev, HCI_OP_RESET, 0, NULL);
 }
=20
+static int verify_valid_bdaddr(struct hci_dev *hdev)
+{
+	return !bacmp(&hdev->bdaddr, BDADDR_ANY);
+}
+
 static void hci_init_req(struct hci_dev *hdev, unsigned long opt)
 {
 	struct sk_buff *skb;
 	__le16 param;
 	__u8 flt_type;
+	int retries =3D 4;
=20
 	BT_DBG("%s %ld", hdev->name, opt);
=20
@@ -202,33 +208,35 @@ static void hci_init_req(struct hci_dev *hdev, unsign=
ed long opt)
=20
 	/* Mandatory initialization */
=20
=2D	/* Reset */
=2D	if (!test_bit(HCI_QUIRK_NO_RESET, &hdev->quirks))
=2D			hci_send_cmd(hdev, HCI_OP_RESET, 0, NULL);
+	do {
+		/* Reset */
+		if (!test_bit(HCI_QUIRK_NO_RESET, &hdev->quirks))
+				hci_send_cmd(hdev, HCI_OP_RESET, 0, NULL);
=20
=2D	/* Read Local Supported Features */
=2D	hci_send_cmd(hdev, HCI_OP_READ_LOCAL_FEATURES, 0, NULL);
+		/* Read Local Supported Features */
+		hci_send_cmd(hdev, HCI_OP_READ_LOCAL_FEATURES, 0, NULL);
=20
=2D	/* Read Local Version */
=2D	hci_send_cmd(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL);
+		/* Read Local Version */
+		hci_send_cmd(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL);
=20
=2D	/* Read Buffer Size (ACL mtu, max pkt, etc.) */
=2D	hci_send_cmd(hdev, HCI_OP_READ_BUFFER_SIZE, 0, NULL);
+		/* Read Buffer Size (ACL mtu, max pkt, etc.) */
+		hci_send_cmd(hdev, HCI_OP_READ_BUFFER_SIZE, 0, NULL);
=20
 #if 0
=2D	/* Host buffer size */
=2D	{
=2D		struct hci_cp_host_buffer_size cp;
=2D		cp.acl_mtu =3D cpu_to_le16(HCI_MAX_ACL_SIZE);
=2D		cp.sco_mtu =3D HCI_MAX_SCO_SIZE;
=2D		cp.acl_max_pkt =3D cpu_to_le16(0xffff);
=2D		cp.sco_max_pkt =3D cpu_to_le16(0xffff);
=2D		hci_send_cmd(hdev, HCI_OP_HOST_BUFFER_SIZE, sizeof(cp), &cp);
=2D	}
+		/* Host buffer size */
+		{
+			struct hci_cp_host_buffer_size cp;
+			cp.acl_mtu =3D cpu_to_le16(HCI_MAX_ACL_SIZE);
+			cp.sco_mtu =3D HCI_MAX_SCO_SIZE;
+			cp.acl_max_pkt =3D cpu_to_le16(0xffff);
+			cp.sco_max_pkt =3D cpu_to_le16(0xffff);
+			hci_send_cmd(hdev, HCI_OP_HOST_BUFFER_SIZE, sizeof(cp), &cp);
+		}
 #endif
=20
=2D	/* Read BD Address */
=2D	hci_send_cmd(hdev, HCI_OP_READ_BD_ADDR, 0, NULL);
+		/* Read BD Address */
+		hci_send_cmd(hdev, HCI_OP_READ_BD_ADDR, 0, NULL);
+	} while(!verify_valid_bdaddr(hdev) && retries--);
=20
 	/* Read Class of Device */
 	hci_send_cmd(hdev, HCI_OP_READ_CLASS_OF_DEV, 0, NULL);
=2D-=20
1.7.1

^ permalink raw reply

* RE: SSP Link key storing issue
From: Par-Gunnar HJALMDAHL @ 2010-07-15 12:10 UTC (permalink / raw)
  To: Waldemar.Rymarkiewicz@tieto.com, luiz.dentz@gmail.com,
	prvb86@motorola.com
  Cc: linux-bluetooth@vger.kernel.org
In-Reply-To: <99B09243E1A5DA4898CDD8B7001114480969964CFC@EXMB04.eu.tieto.com>

Hi,

Basically the reasoning behind all this in the specification is that there is no point in saving the link key on only one side at pairing.
Next connection would then fail anyway.
I agree with Waldemar, this is correct behavior by BlueZ.

Best regards,
Par-Gunnar Hjalmdahl, ST-Ericsson

-----Original Message-----
From: linux-bluetooth-owner@vger.kernel.org [mailto:linux-bluetooth-owner@vger.kernel.org] On Behalf Of Waldemar.Rymarkiewicz@tieto.com
Sent: den 15 juli 2010 10:57
To: luiz.dentz@gmail.com; prvb86@motorola.com
Cc: linux-bluetooth@vger.kernel.org
Subject: RE: SSP Link key storing issue

Hi,

From: linux-bluetooth-owner@vger.kernel.org [mailto:linux-bluetooth-owner@vger.kernel.org] On Behalf Of Luiz Augusto von Dentz
>Hi,

>On Thu, Jul 15, 2010 at 9:38 AM, Prabhakaran Chandrasekara M <prvb86@motorola.com> wrote:
> Can some body please explain why the below check is considered while 
> storing link key.
>
>
> On Thu, Jul 15, 2010 at 10:25 AM, Prabhakaran Chandrasekara M 
> <prvb86@motorola.com> wrote:
>> Hello All,
>>
>>  I am facing some problem with SSP pairing.
>> Sometimes Bluez does not store the Authenticated Combination link key 
>> generated during pairing process And found the below code in 
>> dbus-hci.c hci_dbus_link_key_notify
>>
>> /* Only store the link key if one of the following is true:
>>      * 1. this is a legacy link key
>>      * 2. this is a changed combination key and there was a 
>> previously
>>      *    stored one
>>      * 3. neither local nor remote side had no-bonding as a 
>> requirement
>>      * 4. the local side had dedicated bonding as a requirement
>>      * 5. the remote side is using dedicated bonding since in that 
>> case
>>      *    also the local requirements are set to dedicated bonding
>>      */

> don;t know exactly which page, but the spec says that when one side has no-bonding, I guess 3. is about that, then the link key should not be stored. Also a2dp connection should be using medium security as we do in bluetoothd (it is the default when using BtIO) then you will got >the link key stored properly.


In the spec 2.1 is not clearly stated, but it was explained in the errara to the GAP 2.1
https://www.bluetooth.org/errata/errata_view.cfm?errata_id=2460

You can see also spec to core 3.0  in 6.5.3.1 chapter which says

"When the devices that are performing General Bonding both support Secure
Simple Pairing, the Authentication_Requirements parameter should be set to
MITM Protection Not Required - General Bonding unless the security policy of
an available local service requires MITM Protection in which case the
Authentication_Requirements parameter shall be set to MITM Protection
Required - General Bonding. 'No bonding' is used when the device is performing
a Secure Simple Pairing procedure, but does not intend to retain the link
key after the physical link is disconnected."

In general, this is correct behaviour of Bluez.

Thanks,
Waldek--
To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* [PATCH] Bluetooth: Support for Atheros AR300x serial Bluetooth Chip
From: Suraj Sumangala @ 2010-07-15 10:54 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Jothikumar.Mothilal, Suraj Sumangala

Implements Atheros AR300x Serial HCI protocol.
This protocol extends H4 serial protocol to implement enhanced power management
features supported by Atheros AR300x serial Bluetooth chipsets.

Signed-off-by: Suraj Sumangala <suraj@atheros.com>
---
 drivers/bluetooth/Kconfig     |   12 ++
 drivers/bluetooth/Makefile    |    1 +
 drivers/bluetooth/hci_ath.c   |  241 +++++++++++++++++++++++++++++++++++++++++
 drivers/bluetooth/hci_ldisc.c |    6 +
 drivers/bluetooth/hci_uart.h  |    8 ++-
 5 files changed, 267 insertions(+), 1 deletions(-)
 create mode 100755 drivers/bluetooth/hci_ath.c

diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig
index 058fbcc..c35047f 100644
--- a/drivers/bluetooth/Kconfig
+++ b/drivers/bluetooth/Kconfig
@@ -58,6 +58,18 @@ config BT_HCIUART_BCSP
 
 	  Say Y here to compile support for HCI BCSP protocol.
 
+config BT_HCIUART_ATH3K
+	bool "Atheros AR300x serial Bluetooth support"
+	depends on BT_HCIUART
+	help
+	  HCIATH3K (HCI Atheros AR300x Protocol) is a serial protocol
+	  for communication between host and Atheros AR300x Bluetooth devices.
+	  This protocol enables AR300x Chips to be enabled with
+	  power management support.
+	  Enable this if you have Atheros AR300x serial Bluetooth device.
+
+	  Say Y here to compile support for HCI UART ATH3K protocol.
+
 config BT_HCIUART_LL
 	bool "HCILL protocol support"
 	depends on BT_HCIUART
diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile
index 7e5aed5..71bdf13 100644
--- a/drivers/bluetooth/Makefile
+++ b/drivers/bluetooth/Makefile
@@ -26,4 +26,5 @@ hci_uart-y				:= hci_ldisc.o
 hci_uart-$(CONFIG_BT_HCIUART_H4)	+= hci_h4.o
 hci_uart-$(CONFIG_BT_HCIUART_BCSP)	+= hci_bcsp.o
 hci_uart-$(CONFIG_BT_HCIUART_LL)	+= hci_ll.o
+hci_uart-$(CONFIG_BT_HCIUART_ATH3K)	+= hci_ath.o
 hci_uart-objs				:= $(hci_uart-y)
diff --git a/drivers/bluetooth/hci_ath.c b/drivers/bluetooth/hci_ath.c
new file mode 100755
index 0000000..e8da11b
--- /dev/null
+++ b/drivers/bluetooth/hci_ath.c
@@ -0,0 +1,241 @@
+/*
+ *  Atheros Communication Bluetooth HCIATH UART protocol
+ *
+ *  HCIATH3K (HCI Atheros AR300x Protocol) is a Atheros Communication's
+ *  power management protocol extension to H4 to support AR300x Bluetooth Chip.
+ *
+ *  Copyright (c) 2009-2010 Atheros Communications Inc.
+ *
+ *  Acknowledgements:
+ *  This file is based on hci_h4.c, which was written
+ *  by Maxim Krasnyansky and Marcel Holtmann.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  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.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/errno.h>
+#include <linux/ioctl.h>
+#include <linux/skbuff.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#include "hci_uart.h"
+
+struct ath_struct {
+	struct hci_uart *hu;
+	unsigned int cur_sleep;
+
+	spinlock_t hciath_lock;
+	struct sk_buff *rx_skb;
+	struct sk_buff_head txq;
+	wait_queue_head_t wqevt;
+	struct work_struct ctxtsw;
+};
+
+static int ath_wakeup_ar3k(struct tty_struct *tty)
+{
+	struct termios settings;
+	int status = tty->driver->ops->tiocmget(tty, NULL);
+
+	if (status & TIOCM_CTS)
+		return status;
+
+	/* Disable Automatic RTSCTS */
+	n_tty_ioctl_helper(tty, NULL, TCGETS, (unsigned long)&settings);
+	settings.c_cflag &= ~CRTSCTS;
+	n_tty_ioctl_helper(tty, NULL, TCSETS, (unsigned long)&settings);
+
+	/* Clear RTS first */
+	status = tty->driver->ops->tiocmget(tty, NULL);
+	tty->driver->ops->tiocmset(tty, NULL, 0x00, TIOCM_RTS);
+	mdelay(20);
+
+	/* Set RTS, wake up board */
+	status = tty->driver->ops->tiocmget(tty, NULL);
+	tty->driver->ops->tiocmset(tty, NULL, TIOCM_RTS, 0x00);
+	mdelay(20);
+
+	status = tty->driver->ops->tiocmget(tty, NULL);
+
+	n_tty_ioctl_helper(tty, NULL, TCGETS, (unsigned long)&settings);
+	settings.c_cflag |= CRTSCTS;
+	n_tty_ioctl_helper(tty, NULL, TCSETS, (unsigned long)&settings);
+
+	return status;
+}
+
+static void ath_hci_uart_work(struct work_struct *work)
+{
+	int status;
+	struct ath_struct *ath;
+	struct hci_uart *hu;
+	struct tty_struct *tty;
+
+	ath = container_of(work, struct ath_struct, ctxtsw);
+
+	hu = ath->hu;
+	tty = hu->tty;
+
+	/* verify and wake up controller */
+	if (ath->cur_sleep) {
+		status = ath_wakeup_ar3k(tty);
+		if (!(status & TIOCM_CTS))
+			return;
+	}
+
+	/* Ready to send Data */
+	clear_bit(HCI_UART_SENDING, &hu->tx_state);
+	hci_uart_tx_wakeup(hu);
+}
+
+/* Initialize protocol */
+static int ath_open(struct hci_uart *hu)
+{
+	struct ath_struct *ath;
+
+	BT_DBG("hu %p", hu);
+
+	ath = kzalloc(sizeof(*ath), GFP_ATOMIC);
+	if (!ath)
+		return -ENOMEM;
+
+	skb_queue_head_init(&ath->txq);
+	spin_lock_init(&ath->hciath_lock);
+
+	hu->priv = ath;
+	ath->hu = hu;
+
+	init_waitqueue_head(&ath->wqevt);
+	INIT_WORK(&ath->ctxtsw, ath_hci_uart_work);
+
+	return 0;
+}
+
+/* Flush protocol data */
+static int ath_flush(struct hci_uart *hu)
+{
+	struct ath_struct *ath = hu->priv;
+
+	BT_DBG("hu %p", hu);
+
+	skb_queue_purge(&ath->txq);
+
+	return 0;
+}
+
+/* Close protocol */
+static int ath_close(struct hci_uart *hu)
+{
+	struct ath_struct *ath = hu->priv;
+
+	BT_DBG("hu %p", hu);
+
+	skb_queue_purge(&ath->txq);
+	kfree_skb(ath->rx_skb);
+
+	cancel_work_sync(&ath->ctxtsw);
+
+	hu->priv = NULL;
+	kfree(ath);
+
+	return 0;
+}
+
+#define HCI_OP_ATH_SLEEP 0xFC04
+
+/* Enqueue frame for transmittion */
+static int ath_enqueue(struct hci_uart *hu, struct sk_buff *skb)
+{
+	struct ath_struct *ath = hu->priv;
+
+	if (bt_cb(skb)->pkt_type == HCI_SCODATA_PKT) {
+		kfree(skb);
+		return 0;
+	}
+
+	/*
+	 * Update power management enable flag with parameters of
+	 * HCI sleep enable vendor specific HCI command.
+	 */
+	if (bt_cb(skb)->pkt_type == HCI_COMMAND_PKT) {
+		struct hci_command_hdr *hdr = (void *)skb->data;
+
+		if (__le16_to_cpu(hdr->opcode) == HCI_OP_ATH_SLEEP)
+			ath->cur_sleep = skb->data[HCI_COMMAND_HDR_SIZE];
+	}
+
+	BT_DBG("hu %p skb %p", hu, skb);
+
+	/* Prepend skb with frame type */
+	memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
+
+	skb_queue_tail(&ath->txq, skb);
+	set_bit(HCI_UART_SENDING, &hu->tx_state);
+
+	schedule_work(&ath->ctxtsw);
+
+	return 0;
+}
+
+static struct sk_buff *ath_dequeue(struct hci_uart *hu)
+{
+	struct ath_struct *ath = hu->priv;
+
+	return skb_dequeue(&ath->txq);
+}
+
+/* Recv data */
+static int ath_recv(struct hci_uart *hu, void *data, int count)
+{
+	if (hci_recv_stream_fragment(hu->hdev, data, count) < 0)
+		BT_ERR("Frame Reassembly Failed");
+
+	return count;
+}
+
+static struct hci_uart_proto athp = {
+	.id = HCI_UART_ATH,
+	.open = ath_open,
+	.close = ath_close,
+	.recv = ath_recv,
+	.enqueue = ath_enqueue,
+	.dequeue = ath_dequeue,
+	.flush = ath_flush,
+};
+
+int ath_init(void)
+{
+	int err = hci_uart_register_proto(&athp);
+
+	if (!err)
+		BT_INFO("HCIATH protocol initialized");
+	else
+		BT_ERR("HCIATH protocol registration failed");
+
+	return err;
+}
+
+int ath_deinit(void)
+{
+	return hci_uart_unregister_proto(&athp);
+}
diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c
index a57dbfc..998833d 100644
--- a/drivers/bluetooth/hci_ldisc.c
+++ b/drivers/bluetooth/hci_ldisc.c
@@ -552,6 +552,9 @@ static int __init hci_uart_init(void)
 #ifdef CONFIG_BT_HCIUART_LL
 	ll_init();
 #endif
+#ifdef CONFIG_BT_HCIUART_ATH3K
+	ath_init();
+#endif
 
 	return 0;
 }
@@ -569,6 +572,9 @@ static void __exit hci_uart_exit(void)
 #ifdef CONFIG_BT_HCIUART_LL
 	ll_deinit();
 #endif
+#ifdef CONFIG_BT_HCIUART_ATH3K
+	ath_deinit();
+#endif
 
 	/* Release tty registration of line discipline */
 	if ((err = tty_unregister_ldisc(N_HCI)))
diff --git a/drivers/bluetooth/hci_uart.h b/drivers/bluetooth/hci_uart.h
index 9694d9d..107604e 100644
--- a/drivers/bluetooth/hci_uart.h
+++ b/drivers/bluetooth/hci_uart.h
@@ -35,13 +35,14 @@
 #define HCIUARTGETFLAGS		_IOR('U', 204, int)
 
 /* UART protocols */
-#define HCI_UART_MAX_PROTO	5
+#define HCI_UART_MAX_PROTO	6
 
 #define HCI_UART_H4	0
 #define HCI_UART_BCSP	1
 #define HCI_UART_3WIRE	2
 #define HCI_UART_H4DS	3
 #define HCI_UART_LL	4
+#define HCI_UART_ATH	5
 
 #define HCI_UART_RAW_DEVICE	0
 
@@ -96,3 +97,8 @@ int bcsp_deinit(void);
 int ll_init(void);
 int ll_deinit(void);
 #endif
+
+#ifdef CONFIG_BT_HCIUART_ATH3K
+int ath_init(void);
+int ath_deinit(void);
+#endif
-- 
1.7.0.4


^ permalink raw reply related

* RE: SSP Link key storing issue
From: Waldemar.Rymarkiewicz @ 2010-07-15  8:56 UTC (permalink / raw)
  To: luiz.dentz, prvb86; +Cc: linux-bluetooth
In-Reply-To: <AANLkTimrBSHiwdGvJbQLnIB4dVIkUU6GiUFE6OQo-9WF@mail.gmail.com>

Hi,

From: linux-bluetooth-owner@vger.kernel.org [mailto:linux-bluetooth-owner@vger.kernel.org] On Behalf Of Luiz Augusto von Dentz
>Hi,

>On Thu, Jul 15, 2010 at 9:38 AM, Prabhakaran Chandrasekara M <prvb86@motorola.com> wrote:
> Can some body please explain why the below check is considered while 
> storing link key.
>
>
> On Thu, Jul 15, 2010 at 10:25 AM, Prabhakaran Chandrasekara M 
> <prvb86@motorola.com> wrote:
>> Hello All,
>>
>>  I am facing some problem with SSP pairing.
>> Sometimes Bluez does not store the Authenticated Combination link key 
>> generated during pairing process And found the below code in 
>> dbus-hci.c hci_dbus_link_key_notify
>>
>> /* Only store the link key if one of the following is true:
>>      * 1. this is a legacy link key
>>      * 2. this is a changed combination key and there was a 
>> previously
>>      *    stored one
>>      * 3. neither local nor remote side had no-bonding as a 
>> requirement
>>      * 4. the local side had dedicated bonding as a requirement
>>      * 5. the remote side is using dedicated bonding since in that 
>> case
>>      *    also the local requirements are set to dedicated bonding
>>      */

> don;t know exactly which page, but the spec says that when one side has no-bonding, I guess 3. is about that, then the link key should not be stored. Also a2dp connection should be using medium security as we do in bluetoothd (it is the default when using BtIO) then you will got >the link key stored properly.


In the spec 2.1 is not clearly stated, but it was explained in the errara to the GAP 2.1
https://www.bluetooth.org/errata/errata_view.cfm?errata_id=2460

You can see also spec to core 3.0  in 6.5.3.1 chapter which says

"When the devices that are performing General Bonding both support Secure
Simple Pairing, the Authentication_Requirements parameter should be set to
MITM Protection Not Required - General Bonding unless the security policy of
an available local service requires MITM Protection in which case the
Authentication_Requirements parameter shall be set to MITM Protection
Required - General Bonding. 'No bonding' is used when the device is performing
a Secure Simple Pairing procedure, but does not intend to retain the link
key after the physical link is disconnected."

In general, this is correct behaviour of Bluez.

Thanks,
Waldek

^ permalink raw reply

* Re: SSP Link key storing issue
From: Luiz Augusto von Dentz @ 2010-07-15  8:46 UTC (permalink / raw)
  To: Prabhakaran Chandrasekara M; +Cc: linux-bluetooth
In-Reply-To: <AANLkTiluu5TW1IqAEJrzH9iO0QbJID1G19pd-7TSZ1sG@mail.gmail.com>

Hi,

On Thu, Jul 15, 2010 at 9:38 AM, Prabhakaran Chandrasekara M
<prvb86@motorola.com> wrote:
> Can some body please explain why the below check is considered while
> storing link key.
>
>
> On Thu, Jul 15, 2010 at 10:25 AM, Prabhakaran Chandrasekara M
> <prvb86@motorola.com> wrote:
>> Hello All,
>>
>>  I am facing some problem with SSP pairing.
>> Sometimes Bluez does not store the Authenticated Combination link key
>> generated during pairing process And found the below code in
>> dbus-hci.c
>> hci_dbus_link_key_notify
>>
>> /* Only store the link key if one of the following is true:
>>      * 1. this is a legacy link key
>>      * 2. this is a changed combination key and there was a previously
>>      *    stored one
>>      * 3. neither local nor remote side had no-bonding as a requirement
>>      * 4. the local side had dedicated bonding as a requirement
>>      * 5. the remote side is using dedicated bonding since in that case
>>      *    also the local requirements are set to dedicated bonding
>>      */

I don;t know exactly which page, but the spec says that when one side
has no-bonding, I guess 3. is about that, then the link key should not
be stored. Also a2dp connection should be using medium security as we
do in bluetoothd (it is the default when using BtIO) then you will got
the link key stored properly.


-- 
Luiz Augusto von Dentz
Computer Engineer

^ permalink raw reply

* [PATCH 2/3] Add a2mp protocol/AMP manager, by Atheros Linux BT3 team.
From: Dan Tian @ 2010-07-15  7:39 UTC (permalink / raw)
  To: linux-bluetooth@vger.kernel.org, Haijun Liu, Luis Rodriguez

>From d94973250b15e03cae3299f6828f46d29e19bf33 Mon Sep 17 00:00:00 2001
From: Haijun.Liu <Haijun.Liu@Atheros.com>
Date: Wed, 14 Jul 2010 22:51:05 +0800
Subject: [PATCH 2/3] Add a2mp protocol/AMP manager, by Atheros Linux BT3 team.


Signed-off-by: Haijun.Liu <Haijun.Liu@Atheros.com>
---
 include/net/bluetooth/ampmgr.h |  309 ++++++
 net/bluetooth/ampmgr.c         | 2217 ++++++++++++++++++++++++++++++++++++++++
 2 files changed, 2526 insertions(+), 0 deletions(-)
 create mode 100755 include/net/bluetooth/ampmgr.h
 create mode 100755 net/bluetooth/ampmgr.c

diff --git a/include/net/bluetooth/ampmgr.h b/include/net/bluetooth/ampmgr.h
new file mode 100755
index 0000000..6fe752a
--- /dev/null
+++ b/include/net/bluetooth/ampmgr.h
@@ -0,0 +1,309 @@
+#ifndef _AMPMGR_H
+#define _AMPMGR_H
+
+#define AMP_COMMAND_REJ                0x01
+#define AMP_DISCOVER_REQ       0x02
+#define AMP_DISCOVER_RSP       0x03
+#define AMP_CHANGE_NOTIFY      0x04
+#define AMP_CHANGE_RSP         0x05
+#define AMP_INFO_REQ           0x06
+#define AMP_INFO_RSP           0x07
+#define AMP_ASSOC_REQ          0x08
+#define AMP_ASSOC_RSP          0x09
+#define AMP_CREATE_PHY_LINK_REQ        0x0A
+#define AMP_CREATE_PHY_LINK_RSP        0x0B
+#define AMP_DISC_PHY_LINK_REQ  0x0C
+#define AMP_DISC_PHY_LINK_RSP  0x0D
+
+
+struct a2mp_cmd_hdr {
+       __u8       code;
+       __u8       ident;
+       __le16     len;
+} __attribute__ ((packed));
+
+#define AMP_CMD_HDR_LENGTH     0x04
+
+
+struct a2mp_cmd_rej {
+       __le16  reason;
+} __attribute__ ((packed));
+
+#define AMP_DEFAULT_MTU                670
+#define AMP_DEFAULT_EXT_FEAT_MASK 0
+
+struct a2mp_discover_req {
+       __le16  mtu;
+       __le16  ext_feature_mask;
+} __attribute__ ((packed));
+
+struct a2mp_discover_rsp {
+       __le16  mtu;
+       __le16  ext_feature_mask;
+       __u8    data[0];
+} __attribute__ ((packed));
+
+struct a2mp_change_notify {
+       __u8    data[0];
+} __attribute__ ((packed));
+
+struct a2mp_info_req {
+       __u8    ctrl_id;
+} __attribute__ ((packed));
+
+struct a2mp_info_rsp {
+       __u8    ctrl_id;
+       __u8    status;
+       __le32  total_bandwidth;
+       __le32  max_bandwidth;
+       __le32  min_latency;
+       __le16  pal_capability;
+       __le16  amp_assoc_size;
+} __attribute__ ((packed));
+
+struct a2mp_assoc_req {
+       __u8    ctrl_id;
+} __attribute__ ((packed));
+
+struct a2mp_assoc_rsp {
+       __u8    ctrl_id;
+       __u8    status;
+       __u8    assoc[0];
+} __attribute__ ((packed));
+
+struct a2mp_phy_link_req{
+       __u8    local_ctrl_id;
+       __u8    remote_ctrl_id;
+       __u8    assoc[0];
+} __attribute__ ((packed));
+
+struct a2mp_phy_link_rsp{
+       __u8    local_ctrl_id;
+       __u8    remote_ctrl_id;
+       __u8    status;
+} __attribute__ ((packed));
+
+struct a2mp_disc_phy_link_req{
+       __u8    local_ctrl_id;
+       __u8    remote_ctrl_id;
+} __attribute__ ((packed));
+
+struct a2mp_disc_phy_link_rsp{
+       __u8    local_ctrl_id;
+       __u8    remote_ctrl_id;
+       __u8    status;
+} __attribute__ ((packed));
+
+
+#define AMP_STATUS_OK                          0x00
+#define AMP_STATUS_INVALID_CONTROLLER_ID       0x01
+#define AMP_STATUS_UNABLE_TO_CREATE_LINK       0x02
+#define AMP_STATUS_COLLISION_OCCURRED          0x03
+#define AMP_STATUS_DISC_PACKET_RECV            0x04
+#define AMP_STATUS_PHYSICAL_LINK_EXIST         0x05
+#define AMP_STATUS_INSUFFICIENT_SECURITY       0x06
+
+/* failure for Disconnect/Cancel Physical Link */
+#define AMP_STATUS_NO_PHYSICAL_LINK            0x02
+
+/* l2cap notify amp cmd define */
+#define AMP_HS_REQUEST 0x01
+#define AMP_HS_CANCEL  0x02
+#define AMP_HS_DISC    0x03
+
+/*amp controller available status */
+#define AMP_CONTR_UNKNOWN      0x00
+#define AMP_CONTR_AVAIL                0x01
+#define AMP_CONTR_UNAVAIL      0x02
+
+/*amp controller caps status*/
+#define        AMP_CONTR_POWERED_DOWN          0x00
+#define AMP_CONTR_ONLY_WORKS_WITH_BT   0x01
+#define        AMP_CONTR_NO_CAPACITY           0x02
+#define AMP_CONTR_LOW_CAPACITY         0x03
+#define AMP_CONTR_MEDIUM_CAPACITY      0x04
+#define        AMP_CONTR_HIGH_CAPACITY         0x05
+#define AMP_CONTR_FULL_CAPACITY                0x06
+#define AMP_CONTR_CAP_UNINITIALIZE     0x07
+
+#define DISCOVERY_REQ_SENT     0x01
+#define DISCOVERY_RSP_RECV     0x02
+#define DISCOVERY_REQ_RECV     0x04
+#define DISOCVERY_RSP_SENT     0x08
+#define CHANGE_NOTIFY_SENT     0x10
+#define CHANGE_NOTIFY_RECV     0x20
+
+#define PHY_LINK_IDLE                  0x00
+#define PHY_LINK_INFO_REQ_SENT         0x01
+#define PHY_LINK_ASSOC_REQ_SENT                0x02
+#define PHY_LINK_CREATE_REQ_SENT       0x03
+#define PHY_LINK_CREATE_REQ_RECV       0x04
+#define PHY_LINK_CREATE_RSP_SENT       0x05
+#define PHY_LINK_CREATE_RSP_RECV       0x06
+#define PHY_LINK_HCI_CREATE_SENT       0x07
+#define PHY_LINK_HCI_ACCEPT_SENT       0x08
+#define PHY_LINK_CONNECTED             0x09
+#define PHY_LINK_DISC_REQ_SENT         0x0A
+#define PHY_LINK_DISC_REQ_RECV         0x0B
+#define PHY_LINK_DISC_RSP_SENT         0x0C
+#define PHY_LINK_DISC_RSP_RECV         0x0D
+#define PHY_LINK_HCI_DISC_SENT         0x0E
+
+#define PHY_LINK_MAC_READY             0x01
+
+#define AMP_ROLE_CREATOR       0x01
+#define AMP_ROLE_RESPONDER     0x02
+
+#define PENDING_LOCAL_COMMAND  0x01
+#define PENDING_REMOTE_SIGNAL  0x02
+
+#define REGENERATE_GAMP_LK     0x01
+#define        GAMP_LK_VALID           0x02
+
+#define AMP_DISCOV_TIMEOUT     (20000) /* 20 seconds */
+#define AMP_RESPONSE_TIMEOUT   (20000) /* 20 seconds */
+#define AMP_PHY_LINK_TIMEOUT   (80000) /* 80 seconds */
+
+#define GAMP_KEY_ID            0
+#define PAL_KEY_ID             1
+#define ECMA_KEY_ID            2
+
+#define MAX_PAL_TYPE           3
+
+/* amp link key calc in kernel */
+#define GAMP_KEY_LENGTH                32
+#define SHA256_DIGEST_LEN      32
+
+struct controller_info {
+       __u8    id;
+       __u8    type;
+       __u8    status;
+       __u32   total_bandwidth;
+       __u32   max_bandwidth;
+       __u32   min_latency;
+       __u16   pal_capability;
+       __u16   amp_assoc_size;
+};
+
+struct controller_array {
+       __u32                   size;
+       struct  controller_info contr[0];
+};
+
+struct amp_phy_link {
+       struct hci_phy_link     *hci_link;
+       /*list in a2mp_conn*/
+       struct list_head        per_conn_list;
+       /*list in controller*/
+       struct list_head        per_contr_list;
+       struct a2mp_conn        *a2mp_conn;
+       struct amp_controller   *local;
+       atomic_t                refcnt;
+       __u8                    local_ctrl_id;
+       __u8                    remote_ctrl_id;
+       __u8                    handle;
+       /*identify to track phy_link when receive rsp signal*/
+       __u8                    identify;
+       /*phylink state*/
+       __u8                    state;
+       struct timer_list       a2mp_timer;
+       struct timer_list       timer;
+       __u8                    flag;
+};
+
+struct amp_phy_link_list {
+       struct list_head        head;
+       rwlock_t                lock;
+       __u8                    num;
+};
+
+struct amp_link_key_wrapper {
+       __u8                    valid;
+       struct amp_link_key     key;
+};
+
+struct a2mp_conn {
+       struct l2cap_conn               *conn;
+       spinlock_t                      lock;
+       struct list_head                list;
+       struct controller_array         *remote;
+       /*phy_link list per conn */
+       struct amp_phy_link_list        phy_link_list;
+       atomic_t                        req_cnt;
+       __u16                           mtu;
+       __u16                           feat_mask;
+       /*tx identify*/
+       __u8                            tx_ident;
+       /*remote amp contr avail status */
+       __u8                            contr_state;
+       __u8                            amp_role;
+       __u8                            discov_state;
+       struct timer_list               discov_timer;
+       __u8                            gamp_lk[GAMP_KEY_LENGTH];
+       struct amp_link_key_wrapper     dedicated_lk[MAX_PAL_TYPE];
+       __u8                            status;
+};
+
+struct cmd_context {
+       struct list_head        list;
+       struct a2mp_conn        *a2mp_conn;
+       __u8                    type;
+       __u8                    code;
+       __u8                    identify;
+       __u16                   cmd;
+       __u8                    handle;
+};
+
+struct cmd_context_list {
+       struct list_head head;
+       rwlock_t        lock;
+       __u8            num;
+};
+
+struct amp_controller {
+       /*amp device*/
+       struct hci_dev                  *dev;
+       struct list_head                list;
+       /*phy_link list per amp controller */
+       struct amp_phy_link_list        phy_link_list;
+       struct controller_info          info;
+       /*pending req context queue to handle in cmd complete*/
+       struct cmd_context_list         context_q;
+};
+
+struct amp_controller_list {
+       struct list_head        head;
+       rwlock_t                lock;
+       __u8                    num;
+};
+
+struct amp_a2mp_conn_list {
+       struct list_head        head;
+       rwlock_t                lock;
+       __u8                    num;
+};
+
+struct ampmgr_local {
+       /*spinlock to protect this global*/
+       spinlock_t      lock;
+       /*local amp contr available status*/
+       __u8    contr_state;
+       /*list head for local amp controller*/
+       struct amp_controller_list contr_list;
+       struct amp_a2mp_conn_list  a2mp_list;
+       __u8    handle_seed;
+       spinlock_t      crypt_lock;
+       struct crypto_shash *tfm;
+};
+
+int amp_mgr_init(void);
+void amp_mgr_exit(void);
+void amp_notify(struct l2cap_conn *conn, u8 cmd, void * hci_link);
+int amp_a2mp_channel_init(struct l2cap_conn *conn);
+void amp_a2mp_channel_exit(struct l2cap_conn *conn);
+void  amp_a2mp_process(struct l2cap_conn *conn, struct sk_buff *skb);
+__u8 amp_get_loc_ctrlid_by_phylink(struct l2cap_conn *conn, void * phylink);
+__u8 amp_get_rem_ctrlid_by_phylink(struct l2cap_conn *conn, void * phylink);
+void *amp_get_phylink_by_loc_ctrlid(struct l2cap_conn *conn, __u8 ctrlid);
+void *amp_get_phylink_by_rem_ctrlid(struct l2cap_conn *conn, __u8 ctrlid);
+#endif
diff --git a/net/bluetooth/ampmgr.c b/net/bluetooth/ampmgr.c
new file mode 100755
index 0000000..fb706e1
--- /dev/null
+++ b/net/bluetooth/ampmgr.c
@@ -0,0 +1,2217 @@
+/* Bluetooth amp-mgr */
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/errno.h>
+#include <net/sock.h>
+#include <linux/skbuff.h>
+#include <linux/notifier.h>
+#include <linux/crypto.h>
+#include <crypto/hash.h>
+#include <linux/err.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci.h>
+#include <net/bluetooth/hci_core.h>
+#include <net/bluetooth/l2cap.h>
+#include <net/bluetooth/ampmgr.h>
+
+static struct ampmgr_local ampmgr;
+
+static inline void amp_contr_list_link(struct amp_controller_list *l,
+                                               struct amp_controller *contr)
+{
+       write_lock_bh(&l->lock);
+       list_add_tail(&contr->list, &l->head);
+       l->num++;
+       write_unlock_bh(&l->lock);
+}
+
+static inline void amp_contr_list_unlink(struct amp_controller_list *l,
+                                               struct amp_controller *contr)
+{
+       write_lock_bh(&l->lock);
+       list_del(&contr->list);
+       l->num--;
+       write_unlock_bh(&l->lock);
+}
+
+static inline void amp_a2mp_phylink_link(struct amp_phy_link_list *l,
+                                               struct amp_phy_link *phy_link)
+{
+       write_lock(&l->lock);
+       list_add(&phy_link->per_conn_list, &l->head);
+       write_unlock(&l->lock);
+}
+
+static inline void amp_a2mp_phylink_unlink(struct amp_phy_link_list *l,
+                                               struct amp_phy_link *phy_link)
+{
+
+       write_lock(&l->lock);
+       list_del(&phy_link->per_conn_list);
+       write_unlock(&l->lock);
+}
+
+static inline void amp_a2mp_conn_link(struct amp_a2mp_conn_list *l,
+                                               struct a2mp_conn *a2mp_conn)
+{
+       write_lock(&l->lock);
+       list_add(&a2mp_conn->list, &l->head);
+       write_unlock(&l->lock);
+}
+
+static inline void amp_a2mp_conn_unlink(struct amp_a2mp_conn_list *l,
+                                               struct a2mp_conn *a2mp_conn)
+{
+
+       write_lock_bh(&l->lock);
+       list_del(&a2mp_conn->list);
+       write_unlock_bh(&l->lock);
+}
+
+static inline void amp_contr_phylink_link(struct amp_phy_link_list *l,
+                                               struct amp_phy_link *phy_link)
+{
+       write_lock_bh(&l->lock);
+       list_add(&phy_link->per_contr_list, &l->head);
+       write_unlock_bh(&l->lock);
+}
+
+static inline void amp_contr_phylink_unlink(struct amp_phy_link_list *l,
+                                               struct amp_phy_link *phy_link)
+{
+
+       write_lock_bh(&l->lock);
+       list_del(&phy_link->per_contr_list);
+       write_unlock_bh(&l->lock);
+}
+
+static inline void amp_ctx_list_link(struct cmd_context_list *l,
+                                       struct cmd_context *ctx)
+{
+       write_lock_bh(&l->lock);
+       list_add_tail(&ctx->list, &l->head);
+       write_unlock_bh(&l->lock);
+}
+
+static inline void amp_ctx_list_unlink(struct cmd_context_list *l,
+                                               struct cmd_context *ctx)
+{
+       write_lock_bh(&l->lock);
+       list_del(&ctx->list);
+       write_unlock_bh(&l->lock);
+}
+
+/*Fix me*/
+/* a simple logic to selelct local control for phy link create*/
+static  u8 amp_select_local_contr_id(void)
+{
+       struct amp_controller *amp_contr;
+
+       /* maybe we can select the ctrler according to the AMP status */
+       if (ampmgr.contr_list.num > 0) {
+               amp_contr = list_first_entry(&ampmgr.contr_list.head,
+                                               struct amp_controller, list);
+               return amp_contr->info.id;
+       } else {
+               BT_ERR("error in amp_select_remote_contr_id");
+               return 0;
+       }
+}
+
+/*Fix me*/
+/* a simple logic to selelct remote control for phy link create*/
+static u8 amp_select_remote_contr_id(struct a2mp_conn *a2mp_conn)
+{
+       struct controller_array         *remote = a2mp_conn->remote;
+       /* maybe we can select the ctrler according to the AMP status */
+       if (remote->size > 0) {
+               return remote->contr[0].id;
+       } else {
+               BT_ERR("error in amp_select_remote_contr_id");
+               return 0;
+       }
+}
+
+static struct amp_controller *amp_get_contr_by_id(u8 ctrlid)
+{
+       struct amp_controller *amp_contr = NULL;
+       struct list_head *p;
+
+       read_lock(&ampmgr.contr_list.lock);
+       list_for_each(p, &ampmgr.contr_list.head) {
+               amp_contr = list_entry(p, struct amp_controller, list);
+               if (amp_contr->info.id == ctrlid) {
+                       read_unlock(&ampmgr.contr_list.lock);
+                       return amp_contr;
+               }
+       }
+       read_unlock(&ampmgr.contr_list.lock);
+
+       return NULL;
+}
+
+static struct amp_phy_link *amp_get_phylink_by_ctrlid
+               (struct a2mp_conn *a2mp_conn,
+                       u8 local_ctrl_id, u8 remote_ctrl_id)
+{
+       struct amp_phy_link *phy_link = NULL;
+       struct list_head *p;
+
+       read_lock(&a2mp_conn->phy_link_list.lock);
+
+       list_for_each(p, &a2mp_conn->phy_link_list.head) {
+               phy_link = list_entry(p, struct amp_phy_link, per_conn_list);
+               if ((phy_link->local_ctrl_id == local_ctrl_id) &&
+                       (phy_link->remote_ctrl_id == remote_ctrl_id)) {
+                       read_unlock(&a2mp_conn->phy_link_list.lock);
+                       return phy_link;
+               }
+       }
+       read_unlock(&a2mp_conn->phy_link_list.lock);
+
+       return NULL;
+}
+
+static struct amp_phy_link *amp_get_phylink_by_iden
+                       (struct a2mp_conn *a2mp_conn, u8 identify)
+{
+       struct amp_phy_link *phy_link = NULL;
+       struct list_head *p;
+
+       read_lock(&a2mp_conn->phy_link_list.lock);
+
+       list_for_each(p, &a2mp_conn->phy_link_list.head) {
+               phy_link = list_entry(p, struct amp_phy_link, per_conn_list);
+               if (phy_link->identify == identify) {
+                       read_unlock(&a2mp_conn->phy_link_list.lock);
+                       return phy_link;
+               }
+       }
+       read_unlock(&a2mp_conn->phy_link_list.lock);
+
+       return NULL;
+}
+
+static struct amp_phy_link *amp_get_phylink_by_handle
+                               (struct a2mp_conn *a2mp_conn, u8 handle)
+{
+       struct amp_phy_link *phy_link = NULL;
+       struct list_head *p;
+
+       read_lock(&a2mp_conn->phy_link_list.lock);
+
+       list_for_each(p, &a2mp_conn->phy_link_list.head) {
+               phy_link = list_entry(p, struct amp_phy_link, per_conn_list);
+               if (phy_link->handle == handle) {
+                       read_unlock(&a2mp_conn->phy_link_list.lock);
+                       return phy_link;
+               }
+       }
+       read_unlock(&a2mp_conn->phy_link_list.lock);
+
+       return NULL;
+}
+
+static struct amp_phy_link *amp_get_creator_progress_phy_link
+                               (struct a2mp_conn *a2mp_conn)
+{
+       struct amp_phy_link *phy_link = NULL;
+       struct list_head *p;
+
+       if (a2mp_conn->amp_role != AMP_ROLE_CREATOR)
+               return NULL;
+
+       read_lock(&a2mp_conn->phy_link_list.lock);
+
+       list_for_each(p, &a2mp_conn->phy_link_list.head) {
+               phy_link = list_entry(p, struct amp_phy_link, per_conn_list);
+               if ((phy_link->state == PHY_LINK_INFO_REQ_SENT)
+                       || (phy_link->state == PHY_LINK_ASSOC_REQ_SENT)
+                       || (phy_link->state == PHY_LINK_CREATE_REQ_SENT)
+                       || (phy_link->state == PHY_LINK_HCI_CREATE_SENT)) {
+                       read_unlock(&a2mp_conn->phy_link_list.lock);
+                       return phy_link;
+               }
+       }
+       read_unlock(&a2mp_conn->phy_link_list.lock);
+
+       return NULL;
+}
+
+__u8 amp_get_loc_ctrlid_by_phylink(struct l2cap_conn *conn, void * phylink)
+{
+       struct a2mp_conn *mgr = (struct a2mp_conn *)conn->amp_handle;
+       struct amp_phy_link *link;
+       struct list_head *p;
+       __u8 any = (phylink == NULL) ? 1 : 0;
+
+       read_lock(&mgr->phy_link_list.lock);
+       list_for_each(p, &mgr->phy_link_list.head) {
+               link = list_entry(p, struct amp_phy_link, per_conn_list);
+               if (any || (link->hci_link == phylink)) {
+                       read_unlock(&mgr->phy_link_list.lock);
+
+                       return link->local_ctrl_id;
+               }
+       }
+       read_unlock(&mgr->phy_link_list.lock);
+
+       return CTRL_ID_BREDR;
+}
+
+ __u8 amp_get_rem_ctrlid_by_phylink(struct l2cap_conn *conn, void * phylink)
+{
+       struct a2mp_conn *mgr = (struct a2mp_conn *)conn->amp_handle;
+       struct amp_phy_link *link;
+       struct list_head *p;
+       __u8 any = (phylink == NULL) ? 1 : 0;
+
+       read_lock(&mgr->phy_link_list.lock);
+       list_for_each(p, &mgr->phy_link_list.head) {
+               link = list_entry(p, struct amp_phy_link, per_conn_list);
+               if (any || (link->hci_link == phylink)) {
+                       read_unlock(&mgr->phy_link_list.lock);
+
+                       return link->remote_ctrl_id;
+               }
+       }
+       read_unlock(&mgr->phy_link_list.lock);
+
+       return CTRL_ID_BREDR;
+}
+
+void *amp_get_phylink_by_loc_ctrlid(struct l2cap_conn *conn, __u8 ctrlid)
+{
+       struct a2mp_conn *mgr = (struct a2mp_conn *)conn->amp_handle;
+       struct amp_phy_link *link;
+       struct list_head *p;
+
+       if (ctrlid == CTRL_ID_BREDR)
+               return NULL;
+
+       read_lock(&mgr->phy_link_list.lock);
+       list_for_each(p, &mgr->phy_link_list.head) {
+               link = list_entry(p, struct amp_phy_link, per_conn_list);
+               if (link->local_ctrl_id == ctrlid) {
+                       read_unlock(&mgr->phy_link_list.lock);
+                       return (void *)link->hci_link;
+               }
+       }
+       read_unlock(&mgr->phy_link_list.lock);
+
+       return (void *)NULL;
+}
+
+void *amp_get_phylink_by_rem_ctrlid(struct l2cap_conn *conn, __u8 ctrlid)
+{
+       struct a2mp_conn *mgr = (struct a2mp_conn *)conn->amp_handle;
+       struct amp_phy_link *link;
+       struct list_head *p;
+
+       if (ctrlid == CTRL_ID_BREDR)
+               return NULL;
+
+       read_lock(&mgr->phy_link_list.lock);
+       list_for_each(p, &mgr->phy_link_list.head) {
+               link = list_entry(p, struct amp_phy_link, per_conn_list);
+               if (link->remote_ctrl_id == ctrlid) {
+                       read_unlock(&mgr->phy_link_list.lock);
+                       return (void *)link->hci_link;
+               }
+       }
+       read_unlock(&mgr->phy_link_list.lock);
+
+       return (void *)NULL;
+}
+
+static void amp_set_a2mp_timer(struct amp_phy_link *phy_link, long timeout)
+{
+       BT_DBG("phy link state %d, timeout value %ld",
+                       phy_link->state, timeout);
+       mod_timer(&phy_link->a2mp_timer, jiffies +
+                               msecs_to_jiffies(timeout));
+}
+
+static void amp_clear_a2mp_timer(struct amp_phy_link *phy_link)
+{
+       BT_DBG("phy link state %d", phy_link->state);
+       del_timer(&phy_link->a2mp_timer);
+}
+
+static void amp_set_phylink_timer(struct amp_phy_link *phy_link, long timeout)
+{
+       BT_DBG("phy link state %d, timeout value %ld",
+                       phy_link->state, timeout);
+       mod_timer(&phy_link->timer, jiffies +
+                               msecs_to_jiffies(timeout));
+}
+
+static void amp_clear_phylink_timer(struct amp_phy_link *phy_link)
+{
+       BT_DBG("phy link state %d", phy_link->state);
+       del_timer(&phy_link->timer);
+}
+
+static inline int amp_collision_resolution(bdaddr_t *local, bdaddr_t *remote)
+{
+       int i;
+
+       BT_DBG("resolve collison");
+
+       for (i = 0; i < 6 && (local->b[i] == remote->b[i]); i++)
+               ;
+
+       return local->b[i] > remote->b[i];
+}
+
+static u8 amp_get_ident(struct a2mp_conn *a2mp_conn)
+{
+       u8 id;
+
+       spin_lock_bh(&a2mp_conn->lock);
+
+       if (++a2mp_conn->tx_ident > 128)
+               a2mp_conn->tx_ident = 1;
+
+       id = a2mp_conn->tx_ident;
+
+       spin_unlock_bh(&a2mp_conn->lock);
+
+       return id;
+}
+
+static int hmac_sha256(char *key, size_t klen,
+                       char *data_in, size_t dlen,
+                       char *hash_out, size_t outlen)
+{
+       int rc = 0;
+       struct crypto_shash *tfm = ampmgr.tfm;
+       struct {
+               struct shash_desc shash;
+               char ctx[crypto_shash_descsize(tfm)]; /* size = sizeof(struct shash_desc) + sizeof(struct sha256_state) */
+       } desc;
+
+       if (IS_ERR(tfm)) {
+               BT_DBG("hmac_sha256: crypto_alloc_shash failed");
+               rc = PTR_ERR(tfm);
+               goto err_tfm;
+       }
+       if (crypto_shash_digestsize(tfm) > outlen) {
+               BT_DBG("hmac_sha256: tfm size > result buffer");
+               rc = -EINVAL;
+               goto err_req;
+       }
+       crypto_shash_clear_flags(tfm, -0);
+       rc = crypto_shash_setkey(tfm, key, klen);
+       if (rc) {
+               BT_DBG("hmac_sha256: crypto_shash_setkey failed");
+               goto err_setkey;
+       }
+       desc.shash.tfm = tfm;
+       desc.shash.flags = 0;
+       rc = crypto_shash_digest(&desc.shash, data_in, dlen, hash_out);
+err_setkey:
+err_req:
+       /* crypto_free_shash(tfm); */
+err_tfm:
+       return rc;
+}
+
+static void amp_link_key_h2(u8 *link_key, u8 *digest, u8 key_id)
+{
+
+       u8      tmp[SHA256_DIGEST_LEN];
+
+       if (IS_ERR(ampmgr.tfm))
+               return;
+
+       spin_lock_bh(&ampmgr.crypt_lock);
+
+       switch (key_id) {
+       case GAMP_KEY_ID:
+               hmac_sha256(link_key, GAMP_KEY_LENGTH,
+                       "gamp", 4, tmp, SHA256_DIGEST_LEN);
+               break;
+       case PAL_KEY_ID:
+               hmac_sha256(link_key, GAMP_KEY_LENGTH,
+                       "802b", 4, tmp, SHA256_DIGEST_LEN);
+               break;
+       case ECMA_KEY_ID:
+               hmac_sha256(link_key, GAMP_KEY_LENGTH,
+                       "ecma", 4, tmp, SHA256_DIGEST_LEN);
+               break;
+       default:
+               break;
+       }
+       spin_unlock_bh(&ampmgr.crypt_lock);
+
+       memcpy(digest, tmp, GAMP_KEY_LENGTH);
+ }
+
+static void amp_create_generic_link_key(u8 *link_key, u8 *gamp_link_key)
+{
+       u8      tmp[GAMP_KEY_LENGTH];
+
+       memcpy(tmp, link_key, 16);
+       memcpy(tmp+16, link_key, 16);
+       amp_link_key_h2(tmp , gamp_link_key, GAMP_KEY_ID);
+}
+
+static void amp_create_dedicated_link_key(u8 *gamp_link_key,
+                                       u8 key_type, u8 *decidated_link_key)
+{
+       if (key_type == 0x03)
+               memcpy(decidated_link_key, gamp_link_key, GAMP_KEY_LENGTH);
+       else
+               amp_link_key_h2(gamp_link_key, decidated_link_key, PAL_KEY_ID);
+
+}
+
+struct l2cap_a2mp_packet *amp_build_cmd(struct a2mp_conn *a2mp_conn,
+                                               u8 code, u8 ident, u16 len, void *data)
+{
+       struct l2cap_a2mp_packet *send =  kzalloc((AMP_CMD_HDR_LENGTH + len), GFP_ATOMIC);
+
+       BT_DBG("code 0x%x, ident %d, len %d", code, ident, len);
+
+       if (!send) {
+               BT_DBG("mm alloc fail in amp_build_cmd");
+               return NULL;
+       }
+
+       send->code = code;
+       send->ident = ident;
+       send->len = cpu_to_le16(len);
+       /* Fix me */
+       /* simple logic, assume len < 670 always */
+       if (len > 0)
+               memcpy(send->data, data, len);
+       return  send;
+}
+
+static int amp_build_discover_rsp(struct a2mp_conn *a2mp_conn, void *data)
+{
+       struct a2mp_discover_rsp *rsp = (struct a2mp_discover_rsp *)data;
+       u8      *ptr = rsp->data;
+       struct list_head *p;
+
+       BT_DBG("mtu %d", cpu_to_le16(AMP_DEFAULT_MTU));
+
+       rsp->mtu = cpu_to_le16(AMP_DEFAULT_MTU);
+       rsp->ext_feature_mask = cpu_to_le16(AMP_DEFAULT_EXT_FEAT_MASK);
+
+       *ptr = 0x00;
+       *(ptr+1) = HCI_BREDR;
+       *(ptr+2) = 1;
+       ptr += 3;
+
+       read_lock(&ampmgr.contr_list.lock);
+       list_for_each(p, &ampmgr.contr_list.head) {
+               struct amp_controller *amp_contr = list_entry(p, struct amp_controller, list);
+
+               BT_DBG("AMP controller id %x, tpye %x, status %x",\
+                       amp_contr->info.id, amp_contr->info.type, amp_contr->info.status);
+
+               *ptr = amp_contr->info.id;
+               *(ptr + 1) = amp_contr->info.type;
+#if 1
+               *(ptr + 2) = AMP_CONTR_FULL_CAPACITY;
+#else
+               *(ptr + 2) = amp_contr->info.status;
+#endif
+               ptr += 3;
+       }
+       read_unlock(&ampmgr.contr_list.lock);
+
+       return ptr - (u8 *)data;
+
+}
+
+static int amp_build_change_notify(struct a2mp_conn *a2mp_conn, void *data)
+{
+       struct a2mp_change_notify *noitfy = (struct a2mp_change_notify *)data;
+       u8      *ptr = noitfy->data;
+       struct list_head *p;
+
+       BT_DBG("build change notify");
+
+       *ptr = 0x00;
+       *(ptr+1) = HCI_BREDR;
+       *(ptr+2) = 1;
+       ptr += 3;
+
+       read_lock(&ampmgr.contr_list.lock);
+       list_for_each(p, &ampmgr.contr_list.head) {
+               struct amp_controller *amp_contr = list_entry(p, struct amp_controller, list);
+
+               BT_DBG("AMP controller id %x, tpye %x, status %x",\
+                       amp_contr->info.id, amp_contr->info.type, amp_contr->info.status);
+
+               *ptr = amp_contr->info.id;
+               *(ptr + 1) = amp_contr->info.type;
+#if 1
+               *(ptr + 2) = AMP_CONTR_FULL_CAPACITY;
+#else
+               *(ptr + 2) = amp_contr->info.status;
+#endif
+               ptr += 3;
+       }
+       read_unlock(&ampmgr.contr_list.lock);
+
+       return ptr - (u8 *)data;
+
+}
+
+static int amp_disc_phy_link(struct a2mp_conn *a2mp_conn, struct amp_phy_link *phy_link)
+{
+       struct amp_controller *amp_contr = phy_link->local;
+       struct cmd_context *ctx;
+
+       BT_DBG("disc phy link 0x%p, phy link state %d", phy_link, phy_link->state);
+
+       amp_clear_phylink_timer(phy_link);
+       amp_clear_a2mp_timer(phy_link);
+
+       switch (phy_link->state) {
+       case PHY_LINK_INFO_REQ_SENT:
+       case PHY_LINK_ASSOC_REQ_SENT:
+               break;
+       case PHY_LINK_HCI_CREATE_SENT:
+       case PHY_LINK_CREATE_REQ_SENT:
+       case PHY_LINK_CREATE_RSP_RECV:
+       case PHY_LINK_HCI_ACCEPT_SENT:
+       case PHY_LINK_CREATE_RSP_SENT:
+       case PHY_LINK_CONNECTED:
+       case PHY_LINK_DISC_REQ_SENT:
+               /* send disc phy link cmd */
+               ctx = kzalloc(sizeof(struct cmd_context), GFP_ATOMIC);
+               ctx->type = PENDING_LOCAL_COMMAND;
+               ctx->code = AMP_CREATE_PHY_LINK_REQ;
+               ctx->cmd = HCI_OP_DISCONN_PHYSICAL_LINK;
+               ctx->a2mp_conn = a2mp_conn;
+               ctx->handle = phy_link->handle;
+               amp_ctx_list_link(&amp_contr->context_q, ctx);
+               hci_phylink_put(phy_link->hci_link, 0x13);
+               break;
+       default:
+               break;
+       }
+
+       /* free phy link */
+       amp_a2mp_phylink_unlink(&a2mp_conn->phy_link_list, phy_link);
+       amp_contr_phylink_unlink(&amp_contr->phy_link_list, phy_link);
+       kfree(phy_link);
+
+       BT_DBG("exit");
+
+       return 0;
+}
+
+static void amp_discov_timeout(unsigned long arg)
+{
+       struct a2mp_conn *a2mp_conn = (void *) arg;
+
+       BT_DBG("discovery state %d", a2mp_conn->discov_state);
+}
+
+static void amp_a2mp_timeout(unsigned long arg)
+{
+       struct amp_phy_link *phy_link = (void *) arg;
+
+       BT_DBG("phy_link state %d", phy_link->state);
+
+       if (phy_link == amp_get_creator_progress_phy_link(phy_link->a2mp_conn))
+               phy_link->a2mp_conn->amp_role = AMP_ROLE_RESPONDER;
+
+       amp_disc_phy_link(phy_link->a2mp_conn, phy_link);
+
+       /* FIXME: per spec, the ACL link shall be disconnected */
+}
+
+static void amp_phylink_timeout(unsigned long arg)
+{
+       struct amp_phy_link *phy_link = (void *) arg;
+
+       BT_DBG("phy_link state %d", phy_link->state);
+
+       if (phy_link == amp_get_creator_progress_phy_link(phy_link->a2mp_conn))
+               phy_link->a2mp_conn->amp_role = AMP_ROLE_RESPONDER;
+
+       amp_disc_phy_link(phy_link->a2mp_conn, phy_link);
+}
+
+static int amp_phy_link_add(struct a2mp_conn *a2mp_conn, u8 local_ctrlid, u8 remote_ctrlid)
+{
+       struct amp_phy_link *phy_link;
+       struct amp_controller *amp_contr;
+       struct a2mp_info_req req;
+       struct l2cap_a2mp_packet *send;
+
+       BT_DBG("local ctrl id %d, remote ctrl id %d", local_ctrlid, remote_ctrlid);
+
+       /* dec hs req ,or else phy_link create req will exists always */
+#if    0
+       atomic_dec(&a2mp_conn->req_cnt);
+#endif
+
+       /* only accept one phy link create  simultaneous */
+       if (a2mp_conn->amp_role == AMP_ROLE_CREATOR) {
+               BT_DBG("already in phy create progress");
+               return -1;
+       }
+
+       a2mp_conn->amp_role = AMP_ROLE_CREATOR;
+
+       amp_contr = amp_get_contr_by_id(local_ctrlid);
+       if (!amp_contr)
+               return 0;
+
+       phy_link = kzalloc(sizeof(struct amp_phy_link), GFP_ATOMIC);
+       if (!phy_link)
+               return 0;
+       phy_link->local_ctrl_id = local_ctrlid;
+       phy_link->remote_ctrl_id = remote_ctrlid;
+       phy_link->identify = amp_get_ident(a2mp_conn);
+       phy_link->local = amp_contr;
+       phy_link->a2mp_conn = a2mp_conn;
+
+       setup_timer(&phy_link->a2mp_timer, amp_a2mp_timeout,
+                                               (unsigned long) phy_link);
+       setup_timer(&phy_link->timer, amp_phylink_timeout,
+                                               (unsigned long) phy_link);
+
+       amp_a2mp_phylink_link(&a2mp_conn->phy_link_list, phy_link);
+       amp_contr_phylink_link(&amp_contr->phy_link_list, phy_link);
+
+       req.ctrl_id = remote_ctrlid;
+       send = amp_build_cmd(a2mp_conn, AMP_INFO_REQ, phy_link->identify,
+                               sizeof(struct a2mp_info_req), &req);
+       if (!send)
+               return -1;
+
+       l2cap_a2mp_send_cmd(a2mp_conn->conn, send);
+       kfree(send);
+
+       amp_set_a2mp_timer(phy_link, AMP_RESPONSE_TIMEOUT);
+       amp_set_phylink_timer(phy_link, AMP_PHY_LINK_TIMEOUT);
+
+       phy_link->state = PHY_LINK_INFO_REQ_SENT;
+
+       return 0;
+}
+
+static int amp_contr_event(struct notifier_block *this, unsigned long event, void *ptr)
+{
+       struct amp_controller_list *contr_list = &ampmgr.contr_list;
+       struct hci_dev *hdev = (struct hci_dev *) ptr;
+       struct list_head *head = &contr_list->head, *p, *n, *q;
+       struct amp_controller *amp_contr;
+       struct a2mp_conn *a2mp_conn;
+       struct cmd_context *ctx;
+
+       BT_DBG("device type %d, event id 0x%x", hdev->dev_type, (int)event);
+
+       if (event == HCI_DEV_UP) {
+               u8 contr_id = 1;
+
+               if (hdev->dev_type != HCI_BREDR) {
+                       write_lock_bh(&contr_list->lock);
+                       /* find first available amp ctrl id */
+                       list_for_each(p, &contr_list->head) {
+                               if (list_entry(p, struct amp_controller, list)->info.id != contr_id)
+                                       break;
+                               head = p; contr_id++;
+                       }
+                       amp_contr = kzalloc(sizeof(struct amp_controller), GFP_ATOMIC);
+                       amp_contr->info.id = contr_id;
+                       amp_contr->info.type = hdev->dev_type;
+                       amp_contr->info.status = AMP_CONTR_CAP_UNINITIALIZE;
+                       amp_contr->dev = hdev;
+                       hdev->amp_controller = amp_contr;
+                       rwlock_init(&amp_contr->context_q.lock);
+                       rwlock_init(&amp_contr->phy_link_list.lock);
+                       INIT_LIST_HEAD(&amp_contr->context_q.head);
+                       INIT_LIST_HEAD(&amp_contr->phy_link_list.head);
+                       contr_list->num++;
+                       list_add(&amp_contr->list, head);
+                       write_unlock_bh(&contr_list->lock);
+
+                       read_lock_bh(&ampmgr.a2mp_list.lock);
+                       /* only issue phy link create when local controller from unavailable to available*/
+                       if (ampmgr.contr_state == AMP_CONTR_UNAVAIL) {
+                               list_for_each(p, &ampmgr.a2mp_list.head) {
+                                       a2mp_conn = list_entry(p, struct a2mp_conn, list);
+                                       if ((atomic_read(&a2mp_conn->req_cnt) > 0)
+                                               && (a2mp_conn->contr_state == AMP_CONTR_AVAIL)) {
+                                               u8 local_ctrlid, remote_ctrlid;
+
+                                               local_ctrlid = amp_select_local_contr_id();
+                                               remote_ctrlid = amp_select_remote_contr_id(a2mp_conn);
+                                               amp_phy_link_add(a2mp_conn, local_ctrlid, remote_ctrlid);
+                                       }
+                               }
+                               ampmgr.contr_state = AMP_CONTR_AVAIL;
+                       }
+
+                       list_for_each(p, &ampmgr.a2mp_list.head) {
+                               a2mp_conn = list_entry(p, struct a2mp_conn, list);
+                               if (a2mp_conn->discov_state & DISOCVERY_RSP_SENT) {
+                                       /* need to send change notify if sent discover rsp before*/
+                                       struct cmd_context *ctx;
+
+                                       ctx = kzalloc(sizeof(struct cmd_context), GFP_ATOMIC);
+                                       ctx->type = PENDING_LOCAL_COMMAND;
+                                       /* Fix me, local command, mean need send change notify*/
+                                       ctx->code = AMP_CHANGE_NOTIFY;
+                                       ctx->identify = amp_get_ident(a2mp_conn);
+                                       ctx->cmd = HCI_OP_READ_LOCAL_COMMANDS;
+                                       ctx->a2mp_conn = a2mp_conn;
+
+                                       amp_ctx_list_link(&amp_contr->context_q, ctx);
+                                       hci_read_local_amp_info(amp_contr->dev);
+                               }
+                       }
+
+                       read_unlock_bh(&ampmgr.a2mp_list.lock);
+               }
+       } else if (event == HCI_DEV_DOWN || event == HCI_DEV_UNREG) {
+               struct l2cap_a2mp_packet *send;
+
+               if (hdev->dev_type != HCI_BREDR && hdev->amp_controller) {
+                       struct amp_phy_link *phy_link;
+                       amp_contr = (struct amp_controller *)hdev->amp_controller;
+                       hdev->amp_controller = NULL;
+
+                       write_lock_bh(&contr_list->lock);
+                       list_del(&amp_contr->list);
+                       if (!--contr_list->num)
+                               ampmgr.contr_state = AMP_CONTR_UNAVAIL;
+                       write_unlock_bh(&contr_list->lock);
+
+                       /* free phy link in contr */
+                       write_lock_bh(&amp_contr->phy_link_list.lock);
+                       list_for_each_safe(p, n, &amp_contr->phy_link_list.head) {
+                               phy_link = list_entry(p, struct amp_phy_link, per_contr_list);
+                               if (phy_link->state == PHY_LINK_CONNECTED)
+                                       l2cap_phylink_update(phy_link->a2mp_conn->conn,
+                                               L2CAP_PHYLINK_NOAVL, phy_link->hci_link);
+                               amp_clear_phylink_timer(phy_link);
+                               amp_clear_a2mp_timer(phy_link);
+                               list_del(&phy_link->per_conn_list);
+                               list_del(&phy_link->per_contr_list);
+                               kfree(phy_link);
+                       }
+                       write_unlock_bh(&amp_contr->phy_link_list.lock);
+
+                       /* purge contex_q in contr */
+                       write_lock_bh(&amp_contr->context_q.lock);
+                       list_for_each_safe(q, n, &amp_contr->context_q.head) {
+                                       ctx = list_entry(q, struct cmd_context, list);
+                                       list_del(&ctx->list);
+                                       kfree(ctx);
+                       }
+                       write_unlock_bh(&amp_contr->context_q.lock);
+
+                       /* amp_contr_list_unlink(contr_list, amp_contr); */
+                       kfree(amp_contr);
+
+                       /* need to send change notify if sent discover rsp before */
+                       read_lock_bh(&ampmgr.a2mp_list.lock);
+
+                       /*FIXME: can be optimised,
+                         the build change/cmd are irrelevant with a2mp_conn,
+                         so can be move out of for loop
+                       */
+                       list_for_each(p, &ampmgr.a2mp_list.head) {
+                               a2mp_conn = list_entry(p, struct a2mp_conn, list);
+                               if (a2mp_conn->discov_state & DISOCVERY_RSP_SENT) {
+                                       u8 rsp[64], len;
+
+                                       len = amp_build_change_notify(a2mp_conn, rsp);
+                                       send = amp_build_cmd(a2mp_conn, AMP_CHANGE_NOTIFY,
+                                                       amp_get_ident(a2mp_conn), len, rsp);
+                                       l2cap_a2mp_send_cmd(a2mp_conn->conn, send);
+                                       kfree(send);
+                               }
+                       }
+                       read_unlock_bh(&ampmgr.a2mp_list.lock);
+               }
+       }
+
+       return NOTIFY_DONE;
+}
+
+static struct notifier_block amp_mgr_nblock = {
+       .notifier_call = amp_contr_event
+};
+
+static int amp_create_phy_link_cfm(struct hci_dev *hdev, u8 handle, u8 status)
+{
+       struct amp_controller *amp_contr = (struct amp_controller *)hdev->amp_controller;
+       struct list_head *p, *n;
+       struct cmd_context *ctx;
+       struct amp_phy_link *phy_link;
+       struct l2cap_a2mp_packet *send;
+
+       BT_DBG("amp_contr %p, phy link handle %d, status 0x%x", amp_contr, handle, status);
+
+       if (!amp_contr)
+               return 0;
+
+       write_lock(&amp_contr->context_q.lock);
+       list_for_each_safe(p, n, &amp_contr->context_q.head) {
+               ctx = list_entry(p, struct cmd_context, list);
+               if ((ctx->cmd == HCI_OP_CREATE_PHYSICAL_LINK) && (ctx->handle == handle)) {
+                       phy_link = amp_get_phylink_by_handle(ctx->a2mp_conn, handle);
+                       if (!phy_link) {
+                               BT_ERR("ERROR: dont' exist handle phy_link in amp_create_phy_link");
+                               list_del(&ctx->list);
+                               kfree(ctx);
+                               write_unlock(&amp_contr->context_q.lock);
+                               return 0;
+                       }
+                       BT_DBG("phy_link:%p, phy_link->state:%d", phy_link, phy_link->state);
+                       if ((status == HCI_CREATE_PHYLINK_PEND)
+                               && (phy_link->state == PHY_LINK_HCI_CREATE_SENT)) {
+                               /* fix me */
+                               /* touch hci device here, need think over lock mechansim */
+                               struct amp_assoc        *info = &hdev->local_assoc;
+                               struct a2mp_phy_link_req *req = NULL;
+                               u16 length;
+
+                               BT_DBG("create phy link pend for PHY_LINK_HCI_CREATE_SENT");
+
+                               length = sizeof(struct a2mp_phy_link_req) + info->len;
+                               req = kzalloc(length, GFP_ATOMIC);
+                               req->local_ctrl_id = phy_link->local_ctrl_id;
+                               req->remote_ctrl_id = phy_link->remote_ctrl_id;
+
+                               memcpy(req->assoc, info->data, info->len);
+
+                               phy_link->identify = amp_get_ident(ctx->a2mp_conn);
+                               send = amp_build_cmd(ctx->a2mp_conn, AMP_CREATE_PHY_LINK_REQ,
+                                                       phy_link->identify, length, req);
+                               l2cap_a2mp_send_cmd(ctx->a2mp_conn->conn, send);
+                               phy_link->state = PHY_LINK_CREATE_REQ_SENT;
+                               amp_set_a2mp_timer(phy_link, AMP_RESPONSE_TIMEOUT);
+
+                               kfree(req);
+                               kfree(send);
+                       } else if ((status == HCI_CREATE_PHYLINK_PEND)
+                                       && (phy_link->state == PHY_LINK_HCI_ACCEPT_SENT)) {
+                               struct a2mp_phy_link_rsp rsp;
+
+                               BT_DBG("create phy link pend for PHY_LINK_HCI_ACCEPT_SENT");
+
+                               rsp.local_ctrl_id = amp_contr->info.id;
+                               rsp.remote_ctrl_id = phy_link->remote_ctrl_id;
+                               rsp.status = AMP_STATUS_OK;
+
+                               send = amp_build_cmd(ctx->a2mp_conn, AMP_CREATE_PHY_LINK_RSP,
+                                       ctx->identify, sizeof(struct a2mp_phy_link_rsp), &rsp);
+                               l2cap_a2mp_send_cmd(ctx->a2mp_conn->conn, send);
+                               phy_link->state = PHY_LINK_CREATE_RSP_SENT;
+                               kfree(send);
+                       } else if (!status) {
+                               /* hci phy link create command complete success status */
+                               BT_DBG("phy link mac ready");
+
+                               if (ctx->a2mp_conn->status & REGENERATE_GAMP_LK) {
+                                       __u8 amp_type = amp_contr->dev->dev_type;
+                                       /* regnerate gamp lk */
+                                       ctx->a2mp_conn->status &= ~REGENERATE_GAMP_LK;
+                                       amp_link_key_h2(ctx->a2mp_conn->gamp_lk,
+                                               ctx->a2mp_conn->gamp_lk, GAMP_KEY_ID);
+                                       ctx->a2mp_conn->dedicated_lk[amp_type-1].valid = 1;
+                               }
+                               switch (phy_link->state) {
+                               case PHY_LINK_CREATE_RSP_RECV:
+                               case PHY_LINK_CREATE_RSP_SENT:
+                                       phy_link->state = PHY_LINK_CONNECTED;
+                                       ctx->a2mp_conn->amp_role = AMP_ROLE_RESPONDER;
+                                       amp_clear_phylink_timer(phy_link);
+                                       atomic_dec(&ctx->a2mp_conn->req_cnt);
+                                       l2cap_phylink_update(ctx->a2mp_conn->conn,
+                                               L2CAP_PHYLINK_READY, phy_link->hci_link);
+                                       break;
+                               case PHY_LINK_CREATE_REQ_SENT:
+                                       BT_DBG("phy link create cmd complete before a2mp create rsp");
+                                       phy_link->flag = PHY_LINK_MAC_READY;
+                                       break;
+                               default:
+                                       break;
+                               }
+                               list_del(&ctx->list);
+                               kfree(ctx);
+                       } else  {
+                               BT_ERR("amp phy link create fail, status 0x%x", status);
+                               if (phy_link == amp_get_creator_progress_phy_link(ctx->a2mp_conn))
+                                       ctx->a2mp_conn->amp_role = AMP_ROLE_RESPONDER;
+
+                               if (phy_link->state == PHY_LINK_HCI_ACCEPT_SENT) {
+                                       struct a2mp_phy_link_rsp rsp;
+
+                                       rsp.local_ctrl_id = amp_contr->info.id;
+                                       rsp.remote_ctrl_id = phy_link->remote_ctrl_id;
+                                       rsp.status = AMP_STATUS_UNABLE_TO_CREATE_LINK;
+
+                                       send = amp_build_cmd(ctx->a2mp_conn, AMP_CREATE_PHY_LINK_RSP,
+                                               ctx->identify, sizeof(struct a2mp_phy_link_rsp), &rsp);
+                                       l2cap_a2mp_send_cmd(ctx->a2mp_conn->conn, send);
+                                       phy_link->state = PHY_LINK_CREATE_RSP_SENT;
+                                       kfree(send);
+                               }
+                               list_del(&ctx->list);
+                               kfree(ctx);
+                               amp_clear_phylink_timer(phy_link);
+                               amp_clear_a2mp_timer(phy_link);
+                               list_del(&phy_link->per_conn_list);
+                               list_del(&phy_link->per_contr_list);
+                               kfree(phy_link);
+                       }
+                       break;
+               }
+       }
+       write_unlock(&amp_contr->context_q.lock);
+
+       return 0;
+}
+
+static int amp_disc_phy_link_cfm(struct hci_dev *hdev, u8 handle, u8 status, u8 reason)
+{
+       struct amp_controller *amp_contr = (struct amp_controller *)hdev->amp_controller;
+       struct list_head *p, *n;
+       struct cmd_context *ctx;
+       struct amp_phy_link *phy_link;
+
+       BT_DBG("amp_contr %p, phy link handle %d, status 0x%x", amp_contr, handle, status);
+
+       if (amp_contr) {
+               write_lock(&amp_contr->context_q.lock);
+               list_for_each_safe(p, n, &amp_contr->context_q.head) {
+                       ctx = list_entry(p, struct cmd_context, list);
+                       if ((ctx->cmd == HCI_OP_DISCONN_PHYSICAL_LINK)
+                                        && (ctx->handle == handle)) {
+                               /*todo??*/
+                               list_del(&ctx->list);
+                               kfree(ctx);
+                               break;
+                       }
+               }
+               write_unlock(&amp_contr->context_q.lock);
+       }
+
+       read_lock(&ampmgr.a2mp_list.lock);
+
+       /* for those disc conn can't found ctx, maybe it need global ampmgr to search a2mp_conn*/
+       /* search & found in ampmgr */
+       list_for_each(p, &ampmgr.a2mp_list.head) {
+               struct a2mp_conn *a2mp_conn;
+               a2mp_conn = list_entry(p, struct a2mp_conn, list);
+                       phy_link = amp_get_phylink_by_handle(a2mp_conn, handle);
+                       if (phy_link) {
+
+                               BT_DBG("phy link %p", phy_link);
+                               l2cap_phylink_update(a2mp_conn->conn,
+                                        L2CAP_PHYLINK_NOAVL, phy_link->hci_link);
+                               amp_clear_phylink_timer(phy_link);
+                               amp_clear_a2mp_timer(phy_link);
+                               amp_a2mp_phylink_unlink(&a2mp_conn->phy_link_list, phy_link);
+                               if (amp_contr)
+                                       amp_contr_phylink_unlink(&amp_contr->phy_link_list, phy_link);
+                               kfree(phy_link);
+                       }
+       }
+       read_unlock(&ampmgr.a2mp_list.lock);
+
+       return 0;
+}
+
+static int amp_local_assoc_ind(struct hci_dev *hdev, u8 status)
+{
+       struct amp_controller *amp_contr = (struct amp_controller *)hdev->amp_controller;
+       struct list_head *p, *n;
+       struct cmd_context *ctx;
+
+       BT_DBG("amp_contr %p, status %d", amp_contr, status);
+
+       if (!amp_contr)
+               return 0;
+
+       write_lock(&amp_contr->context_q.lock);
+       list_for_each_safe(p, n, &amp_contr->context_q.head) {
+               ctx = list_entry(p, struct cmd_context, list);
+               if (ctx->cmd == HCI_OP_READ_LOCAL_AMP_ASSOC) {
+                       struct l2cap_a2mp_packet *send;
+                       struct a2mp_assoc_rsp *rsp;
+                       struct amp_assoc                *info = &hdev->local_assoc;
+                       u16 length;
+
+                       BT_DBG("local assoc ind for READ_LOCAL_AMP_ASSOC");
+
+                       length = sizeof(struct a2mp_assoc_rsp) + info->len;
+                       rsp = kzalloc(length, GFP_ATOMIC);
+                       rsp->ctrl_id = amp_contr->info.id;
+                       rsp->status = AMP_STATUS_OK;
+
+                       memcpy(rsp->assoc, info->data, info->len);
+
+                       send = amp_build_cmd(ctx->a2mp_conn, AMP_ASSOC_RSP, ctx->identify,
+                                               length, rsp);
+                       l2cap_a2mp_send_cmd(ctx->a2mp_conn->conn, send);
+                       kfree(send);
+                       kfree(rsp);
+                       list_del(&ctx->list);
+                       kfree(ctx);
+                       break;
+               }
+       }
+       write_unlock(&amp_contr->context_q.lock);
+
+       return 0;
+}
+
+static int amp_local_info_ind(struct hci_dev *hdev, u8 status)
+{
+       struct amp_controller *amp_contr = (struct amp_controller *)hdev->amp_controller;
+       struct list_head *p, *tmp, *n;
+       struct cmd_context *ctx;
+       struct l2cap_a2mp_packet *send;
+
+       BT_DBG("amp_contr %p, status %d", amp_contr, status);
+
+       if (!amp_contr)
+               return 0;
+
+       amp_contr->info.status = hdev->ctrl_info.amp_status;
+       write_lock(&amp_contr->context_q.lock);
+       list_for_each_safe(p, n, &amp_contr->context_q.head) {
+               ctx = list_entry(p, struct cmd_context, list);
+               if (ctx->cmd == HCI_OP_READ_LOCAL_COMMANDS) {
+                       if ((ctx->type == PENDING_REMOTE_SIGNAL)
+                               && (ctx->code == AMP_DISCOVER_REQ)) {
+                               __u8 i = 0;
+
+                               BT_DBG("local info ind for discovery req");
+                               read_lock(&ampmgr.contr_list.lock);
+                               list_for_each(tmp, &ampmgr.contr_list.head) {
+                                       struct amp_controller *contr = list_entry(tmp, struct amp_controller, list);
+                                       if (contr->info.status == AMP_CONTR_CAP_UNINITIALIZE)
+                                               i++;
+                               }
+                               read_unlock(&ampmgr.contr_list.lock);
+                               /* send discover rsp after read all contr status */
+                               if (i == 0) {
+                                       u8 rsp[64], len;
+
+                                       len = amp_build_discover_rsp(ctx->a2mp_conn, rsp);
+                                       send = amp_build_cmd(ctx->a2mp_conn,
+                                                       AMP_DISCOVER_RSP, ctx->identify, len, rsp);
+                                       l2cap_a2mp_send_cmd(ctx->a2mp_conn->conn, send);
+                                       ctx->a2mp_conn->discov_state |= DISOCVERY_RSP_SENT;
+                                       kfree(send);
+                               }
+                       } else if ((ctx->type == PENDING_REMOTE_SIGNAL)
+                                       && (ctx->code == AMP_INFO_REQ)) {
+                               /* fix me*/
+                               /* touch hci device here,need think over lock mechansim */
+                               struct amp_info         info = hdev->ctrl_info;
+                               struct a2mp_info_rsp rsp;
+
+                               BT_DBG("local info ind for info req");
+                               rsp.ctrl_id = amp_contr->info.id;
+                               rsp.status = AMP_STATUS_OK;
+                               rsp.total_bandwidth = cpu_to_le32(info.total_bandwidth);
+                               rsp.max_bandwidth = cpu_to_le32(info.max_guaranteed_bandwidth);
+                               rsp.min_latency = cpu_to_le32(info.min_latency);
+                               rsp.pal_capability = cpu_to_le16(info.pal_caps);
+                               rsp.amp_assoc_size = cpu_to_le16(info.max_assoc_len);
+                               send = amp_build_cmd(ctx->a2mp_conn, AMP_INFO_RSP, ctx->identify,
+                                                       sizeof(struct a2mp_info_rsp), &rsp);
+                               l2cap_a2mp_send_cmd(ctx->a2mp_conn->conn, send);
+                               kfree(send);
+                       }  else if ((ctx->type == PENDING_LOCAL_COMMAND)
+                                       && (ctx->code == AMP_CHANGE_NOTIFY)) {
+                               u8 rsp[64], len;
+
+                               BT_DBG("local info ind for change notify");
+                               len = amp_build_change_notify(ctx->a2mp_conn, rsp);
+                               send = amp_build_cmd(ctx->a2mp_conn,
+                                               AMP_CHANGE_NOTIFY, ctx->identify, len, rsp);
+                               l2cap_a2mp_send_cmd(ctx->a2mp_conn->conn, send);
+                               kfree(send);
+                       }
+
+                       list_del(&ctx->list);
+                       kfree(ctx);
+                       break;
+               } else {
+                       continue;
+               }
+       }
+
+       write_unlock(&amp_contr->context_q.lock);
+       return 0;
+}
+
+static struct hci_cb amp_mgr_cb = {
+       .name                           = "ampmgr",
+       .create_physical_link_cfm       = amp_create_phy_link_cfm,
+       .put_physical_link_cfm          = amp_disc_phy_link_cfm,
+       .local_assoc_ind                = amp_local_assoc_ind,
+       .local_info_ind                 = amp_local_info_ind
+};
+
+/* Fix me */
+/* a simple logic to get physical link handle, maybe get duplicate phy_handle after rewind */
+static u8 amp_get_phy_handle(void)
+{
+       u8 id;
+
+       spin_lock_bh(&ampmgr.lock);
+
+       if (++ampmgr.handle_seed > 256)
+               ampmgr.handle_seed = 1;
+       id = ampmgr.handle_seed;
+       spin_unlock_bh(&ampmgr.lock);
+       return id;
+}
+
+/* register hci call back interface ,
+ * register amp controller hotplug notify block,
+ * collect local amp controller list
+ * init amp link key h2 function ctx
+ */
+int amp_mgr_init(void)
+{
+       struct amp_controller *amp_contr;
+       struct list_head *p;
+       u8      contr_id = 0;
+
+       hci_register_cb(&amp_mgr_cb);
+
+       rwlock_init(&ampmgr.contr_list.lock);
+       rwlock_init(&ampmgr.a2mp_list.lock);
+       spin_lock_init(&ampmgr.lock);
+       spin_lock_init(&ampmgr.crypt_lock);
+
+       INIT_LIST_HEAD(&ampmgr.contr_list.head);
+       INIT_LIST_HEAD(&ampmgr.a2mp_list.head);
+
+       ampmgr.contr_state = AMP_CONTR_UNAVAIL;
+
+       read_lock_bh(&hci_dev_list_lock);
+       list_for_each(p, &hci_dev_list) {
+               struct hci_dev *d = list_entry(p, struct hci_dev, list);
+
+               if (d->dev_type != HCI_BREDR && test_bit(HCI_UP, &d->flags)) {
+                       contr_id++;
+                       /* amp controller exist indicate amp controller available?*/
+                       /* Fix me*/
+                       ampmgr.contr_state = AMP_CONTR_AVAIL;
+
+                       amp_contr = kzalloc(sizeof(struct amp_controller), GFP_KERNEL);
+                       if (amp_contr == NULL) {
+                               read_unlock_bh(&hci_dev_list_lock);
+                               return -1;
+                       }
+                       /* rwlock_init(&amp_contr->lock); */
+
+                       amp_contr->info.id = contr_id;
+                       amp_contr->info.type = d->dev_type;
+                       /* simplify logic here,need refine */
+                       amp_contr->info.status = AMP_CONTR_CAP_UNINITIALIZE;
+                       amp_contr->dev = d;
+                       /* let hci device pointer back */
+                       d->amp_controller = amp_contr;
+                       rwlock_init(&amp_contr->context_q.lock);
+                       rwlock_init(&amp_contr->phy_link_list.lock);
+                       amp_contr_list_link(&ampmgr.contr_list, amp_contr);
+                       INIT_LIST_HEAD(&amp_contr->context_q.head);
+                       INIT_LIST_HEAD(&amp_contr->phy_link_list.head);
+
+               }
+       }
+
+       BT_DBG("amp controller count %d", ampmgr.contr_list.num);
+       read_unlock_bh(&hci_dev_list_lock);
+
+       hci_register_notifier(&amp_mgr_nblock);
+
+       ampmgr.tfm = crypto_alloc_shash("hmac(sha256)", 0, 0);
+       if (IS_ERR(ampmgr.tfm))
+               return -1;
+
+       return 0;
+}
+
+/* call when l2cap module unload */
+void amp_mgr_exit(void)
+{
+       struct list_head *p, *n;
+       struct amp_controller_list *contr_list = &ampmgr.contr_list;
+       struct amp_controller *amp_contr;
+
+       if (!IS_ERR(ampmgr.tfm))
+               crypto_free_shash(ampmgr.tfm);
+
+       hci_unregister_cb(&amp_mgr_cb);
+       hci_unregister_notifier(&amp_mgr_nblock);
+
+       /*Fix me, maybe phy link list in amp controller exist*/
+       write_lock_bh(&contr_list->lock);
+       list_for_each_safe(p, n, &contr_list->head) {
+               amp_contr = list_entry(p, struct amp_controller, list);
+               amp_contr->dev->amp_controller = NULL;
+               list_del(&amp_contr->list);
+               contr_list->num--;
+               kfree(amp_contr);
+       }
+       write_unlock_bh(&contr_list->lock);
+
+       return;
+}
+
+/*init amp_mgr a2mp channel */
+int amp_a2mp_channel_init(struct l2cap_conn *conn)
+{
+       struct a2mp_conn *a2mp_conn;
+
+       BT_DBG("l2cap_conn %p", conn);
+
+       if (conn->amp_handle)
+               return 0;
+       a2mp_conn = kzalloc(sizeof(struct a2mp_conn), GFP_ATOMIC);
+       if (!a2mp_conn)
+               return -1;
+       conn->amp_handle = (void *)a2mp_conn;
+       a2mp_conn->conn = conn;
+
+       rwlock_init(&a2mp_conn->phy_link_list.lock);
+
+       spin_lock_init(&a2mp_conn->lock);
+
+       INIT_LIST_HEAD(&a2mp_conn->phy_link_list.head);
+
+       setup_timer(&a2mp_conn->discov_timer, amp_discov_timeout,
+                                               (unsigned long) a2mp_conn);
+
+       amp_a2mp_conn_link(&ampmgr.a2mp_list, a2mp_conn);
+
+       a2mp_conn->contr_state = AMP_CONTR_UNKNOWN;
+       atomic_set(&a2mp_conn->req_cnt, 0);
+
+       return 0;
+
+}
+
+/*destroy amp_mgr a2mp channel*/
+void amp_a2mp_channel_exit(struct l2cap_conn *conn)
+{
+       struct a2mp_conn *a2mp_conn = (struct a2mp_conn *)conn->amp_handle;
+       struct list_head *p, *q, *n;
+       struct amp_phy_link *phy_link;
+       struct cmd_context *ctx;
+       struct amp_controller *amp_contr;
+
+       BT_DBG("l2cap_conn %p", conn);
+       if (!a2mp_conn)
+               return;
+
+       /*Fix me, need think over phy link list in amp controller*/
+       /*need think over how to disc phy link */
+       write_lock(&a2mp_conn->phy_link_list.lock);
+       list_for_each_safe(p, n, &a2mp_conn->phy_link_list.head) {
+               /*disc phy link without wait cfm*/
+               phy_link = list_entry(p, struct amp_phy_link, per_conn_list);
+               amp_clear_phylink_timer(phy_link);
+               amp_clear_a2mp_timer(phy_link);
+               if (phy_link->hci_link)
+                       hci_phylink_put(phy_link->hci_link, 0x13);
+               list_del(&phy_link->per_conn_list);
+               list_del(&phy_link->per_contr_list);
+               kfree(phy_link);
+       }
+       write_unlock(&a2mp_conn->phy_link_list.lock);
+
+       if (a2mp_conn->remote)
+               kfree(a2mp_conn->remote);
+
+       /*purge cmd ctx that refer a2mp_conn*/
+       read_lock(&ampmgr.contr_list.lock);
+       list_for_each(p, &ampmgr.contr_list.head) {
+               amp_contr = list_entry(p, struct amp_controller, list);
+
+               write_lock(&amp_contr->context_q.lock);
+               list_for_each_safe(q, n, &amp_contr->context_q.head) {
+                       ctx = list_entry(q, struct cmd_context, list);
+                       if (ctx->a2mp_conn == a2mp_conn) {
+                               list_del(&ctx->list);
+                               kfree(ctx);
+                       }
+               }
+               write_unlock(&amp_contr->context_q.lock);
+       }
+       read_unlock(&ampmgr.contr_list.lock);
+
+       if (a2mp_conn->discov_state & DISCOVERY_REQ_SENT)
+               del_timer(&a2mp_conn->discov_timer);
+
+       amp_a2mp_conn_unlink(&ampmgr.a2mp_list, a2mp_conn);
+       kfree(a2mp_conn);
+       conn->amp_handle = NULL;
+       BT_DBG("exit");
+       return ;
+}
+
+/* a simple implememt of amp_notify */
+void amp_notify(struct l2cap_conn *conn, u8 cmd, void * hci_link)
+{
+       struct a2mp_conn *a2mp_conn = conn->amp_handle;
+       struct l2cap_a2mp_packet *send;
+       struct amp_phy_link *phy_link = NULL;
+       struct list_head *p;
+
+       BT_DBG("cmd 0x%4.4x", cmd);
+
+       switch (cmd) {
+       case AMP_HS_REQUEST:
+               atomic_inc(&a2mp_conn->req_cnt);
+               if ((atomic_read(&a2mp_conn->req_cnt) > 0)
+                   && (a2mp_conn->contr_state == AMP_CONTR_AVAIL)
+                   && (ampmgr.contr_state == AMP_CONTR_AVAIL)) {
+                       u8 local_ctrlid, remote_ctrlid;
+
+                       local_ctrlid = amp_select_local_contr_id();
+                       remote_ctrlid = amp_select_remote_contr_id(a2mp_conn);
+                       amp_phy_link_add(a2mp_conn, local_ctrlid, remote_ctrlid);
+               } else {
+                       if (a2mp_conn->contr_state == AMP_CONTR_UNKNOWN) {
+                               struct a2mp_discover_req req;
+
+                               req.mtu = cpu_to_le16(AMP_DEFAULT_MTU);
+                               req.ext_feature_mask = cpu_to_le16(AMP_DEFAULT_EXT_FEAT_MASK);
+
+                               mod_timer(&a2mp_conn->discov_timer, jiffies +
+                                       msecs_to_jiffies(AMP_DISCOV_TIMEOUT));
+
+                               a2mp_conn->discov_state |= DISCOVERY_REQ_SENT;
+
+                               send = amp_build_cmd(a2mp_conn, AMP_DISCOVER_REQ,
+                                               amp_get_ident(a2mp_conn), sizeof(struct a2mp_discover_req), &req);
+                               l2cap_a2mp_send_cmd(a2mp_conn->conn, send);
+                               kfree(send);
+                       }
+               }
+               break;
+       case AMP_HS_CANCEL:
+               /*only exist one phy link in creator progress*/
+               phy_link = amp_get_creator_progress_phy_link(a2mp_conn);
+               if (phy_link) {
+                       a2mp_conn->amp_role = AMP_ROLE_RESPONDER;
+                       amp_disc_phy_link(a2mp_conn, phy_link);
+               } else {
+                       if (a2mp_conn->discov_state & DISCOVERY_REQ_SENT)
+                               del_timer(&a2mp_conn->discov_timer);
+                       atomic_dec(&a2mp_conn->req_cnt);
+               }
+               break;
+       case AMP_HS_DISC:
+               /*this disc notify only receive when phy link connected*/
+               read_lock(&a2mp_conn->phy_link_list.lock);
+               list_for_each(p, &a2mp_conn->phy_link_list.head) {
+                       phy_link = list_entry(p, struct amp_phy_link, per_conn_list);
+                       if (phy_link->hci_link == hci_link)
+                               break;
+               }
+               read_unlock(&a2mp_conn->phy_link_list.lock);
+
+               if ((!phy_link) || (phy_link->hci_link != hci_link)) {
+                       BT_ERR("can't find match phy_link with specify notify");
+                       return;
+               }
+
+               /* Disconnect Physical Link request is an optional request*/
+#if 0
+               req.local_ctrl_id = phy_link->local_ctrl_id;
+               req.remote_ctrl_id = phy_link->remote_ctrl_id;
+
+               phy_link->state = PHY_LINK_DISC_REQ_SENT;
+
+               amp_set_a2mp_timer(phy_link, AMP_RESPONSE_TIMEOUT);
+
+               phy_link->identify = amp_get_ident(a2mp_conn);
+
+               send = amp_build_cmd(a2mp_conn, AMP_DISC_PHY_LINK_REQ, phy_link->identify,
+                                       sizeof(struct a2mp_disc_phy_link_req), &req);
+               l2cap_a2mp_send_cmd(a2mp_conn->conn, send);
+               kfree(send);
+#endif
+               /* notify l2cap only connected phylink*/
+               if (phy_link->state == PHY_LINK_CONNECTED)
+                       l2cap_phylink_update(a2mp_conn->conn, L2CAP_PHYLINK_NOAVL, phy_link->hci_link);
+               amp_disc_phy_link(a2mp_conn, phy_link);
+               break;
+       default:
+               break;
+       }
+
+       return;
+}
+
+/* nothing to do in cmd rej?? */
+static int amp_command_rej(struct a2mp_conn *a2mp_conn, struct a2mp_cmd_hdr *cmd, u8 *data)
+{
+       BT_DBG("command rej, ident %d", cmd->ident);
+       return 0;
+}
+
+static int amp_discover_req(struct a2mp_conn *a2mp_conn, struct a2mp_cmd_hdr *cmd, u8 *data)
+{
+       struct a2mp_discover_req *req = (struct a2mp_discover_req *)data;
+       struct list_head *p;
+       struct amp_controller *amp_contr;
+       u8  j = 0;
+       u16 mtu;
+
+       mtu = __le16_to_cpu(req->mtu);
+
+       BT_DBG("mtu 0x%4.4x", mtu);
+
+       if (mtu < AMP_DEFAULT_MTU)
+               return -1;
+
+       a2mp_conn->discov_state |= DISCOVERY_REQ_RECV;
+       a2mp_conn->mtu = mtu;
+       a2mp_conn->feat_mask = __le16_to_cpu(req->ext_feature_mask);
+
+       read_lock(&ampmgr.contr_list.lock);
+       list_for_each(p, &ampmgr.contr_list.head) {
+               amp_contr = list_entry(p, struct amp_controller, list);
+               if (amp_contr->info.status == AMP_CONTR_CAP_UNINITIALIZE) {
+                       struct cmd_context *ctx;
+
+                       ctx = kzalloc(sizeof(struct cmd_context), GFP_ATOMIC);
+                       if (!ctx) {
+                               BT_ERR("mm alloc fail in amp_discover_req");
+                               read_unlock(&ampmgr.contr_list.lock);
+                               return 0;
+                       }
+                       j++;
+                       ctx->type = PENDING_REMOTE_SIGNAL;
+                       ctx->code = AMP_DISCOVER_REQ;
+                       ctx->identify = cmd->ident;
+                       ctx->cmd = HCI_OP_READ_LOCAL_COMMANDS;
+                       ctx->a2mp_conn = a2mp_conn;
+
+                       amp_ctx_list_link(&amp_contr->context_q, ctx);
+                       hci_read_local_amp_info(amp_contr->dev);
+               }
+
+       }
+       read_unlock(&ampmgr.contr_list.lock);
+
+       /*send discover rsp signal, no need to query controller status via HCI command*/
+       if (j == 0) {
+               struct l2cap_a2mp_packet *send;
+               u8 rsp[64], len;
+
+               len = amp_build_discover_rsp(a2mp_conn, rsp);
+               send = amp_build_cmd(a2mp_conn, AMP_DISCOVER_RSP, cmd->ident, len, rsp);
+               l2cap_a2mp_send_cmd(a2mp_conn->conn, send);
+               a2mp_conn->discov_state |= DISOCVERY_RSP_SENT;
+               kfree(send);
+       }
+
+       return 0;
+}
+
+static int amp_discover_rsp(struct a2mp_conn *a2mp_conn, struct a2mp_cmd_hdr *cmd, u8 *data)
+{
+       struct a2mp_discover_rsp *rsp = (struct a2mp_discover_rsp *)data;
+       struct controller_array *remote;
+       u8 count = 0, i = 0;
+       u8 *buffer = rsp->data;
+       u16 mtu;
+
+       mtu = __le16_to_cpu(rsp->mtu);
+       BT_DBG("mtu 0x%4.4x", mtu);
+
+       del_timer(&a2mp_conn->discov_timer);
+       a2mp_conn->discov_state |= DISCOVERY_RSP_RECV;
+
+       if (mtu < AMP_DEFAULT_MTU) {
+               BT_DBG("response mtu < AMP_MTU ");
+               return -1;
+       }
+
+       a2mp_conn->mtu = mtu;
+       a2mp_conn->feat_mask = __le16_to_cpu(rsp->ext_feature_mask);
+       count = (__le16_to_cpu(cmd->len) - sizeof(struct a2mp_discover_rsp))/3;
+
+       if (a2mp_conn->remote)
+               kfree(a2mp_conn->remote);
+
+       a2mp_conn->contr_state = AMP_CONTR_UNAVAIL;
+       remote = kzalloc((sizeof(struct controller_array) + sizeof(struct controller_info)*(count-1)),
+                               GFP_ATOMIC);
+
+       if (!remote)
+               return 0;
+       /*skip BR/EDR controller*/
+       buffer += 3;
+       remote->size = count-1;
+       for (i = 0; i < count-1; i++) {
+               remote->contr[i].id = *buffer;
+               remote->contr[i].type = *(buffer + 1);
+               remote->contr[i].status = *(buffer + 2);
+
+               if (remote->contr[i].type != HCI_BREDR)
+                       a2mp_conn->contr_state = AMP_CONTR_AVAIL;
+
+               buffer += 3;
+       }
+
+       a2mp_conn->remote = remote;
+
+       if ((atomic_read(&a2mp_conn->req_cnt) > 0)
+               && (a2mp_conn->contr_state == AMP_CONTR_AVAIL)
+               && (ampmgr.contr_state == AMP_CONTR_AVAIL)) {
+                       u8 local_ctrlid, remote_ctrlid;
+
+                       local_ctrlid = amp_select_local_contr_id();
+                       remote_ctrlid = amp_select_remote_contr_id(a2mp_conn);
+                       amp_phy_link_add(a2mp_conn, local_ctrlid, remote_ctrlid);
+       }
+
+       return 0;
+}
+
+static int amp_change_notify(struct a2mp_conn *a2mp_conn, struct a2mp_cmd_hdr *cmd, u8 *data)
+{
+       struct a2mp_change_notify *notify = (struct a2mp_change_notify *)data;
+       struct l2cap_a2mp_packet *send;
+       struct controller_array *remote;
+       u8 count = 0, i = 0;
+       u8 *buffer = notify->data;
+
+       count = (__le16_to_cpu(cmd->len) - sizeof(struct a2mp_change_notify))/3;
+
+       BT_DBG("contr count include br/edr is %d", count);
+
+       if (a2mp_conn->remote)
+               kfree(a2mp_conn->remote);
+
+       a2mp_conn->contr_state = AMP_CONTR_UNAVAIL;
+       remote = kzalloc((sizeof(struct controller_array) +
+                               sizeof(struct controller_info)*(count-1)), GFP_ATOMIC);
+       if (!remote)
+               return 0;
+       buffer += 3;
+       remote->size = count-1;
+       for (i = 0; i < count-1; i++) {
+               remote->contr[i].id = *buffer;
+               remote->contr[i].type = *(buffer + 1);
+               remote->contr[i].status = *(buffer + 2);
+
+               if (remote->contr[i].type != HCI_BREDR) {
+                       /* fix me, AMP_CONTR_AVAIL means exists amp controller?? */
+                       a2mp_conn->contr_state = AMP_CONTR_AVAIL;
+               }
+               buffer += 3;
+       }
+
+       a2mp_conn->remote = remote;
+       if ((atomic_read(&a2mp_conn->req_cnt) > 0)
+               && (a2mp_conn->contr_state == AMP_CONTR_AVAIL)
+               && (ampmgr.contr_state == AMP_CONTR_AVAIL)) {
+                       u8 local_ctrlid, remote_ctrlid;
+
+                       local_ctrlid = amp_select_local_contr_id();
+                       remote_ctrlid = amp_select_remote_contr_id(a2mp_conn);
+                       amp_phy_link_add(a2mp_conn, local_ctrlid, remote_ctrlid);
+       }
+
+       /* send notify rsp*/
+       send = amp_build_cmd(a2mp_conn, AMP_CHANGE_RSP, cmd->ident, 0, NULL);
+       l2cap_a2mp_send_cmd(a2mp_conn->conn, send);
+       kfree(send);
+
+       return 0;
+}
+
+/*nothing to do here????*/
+static int amp_change_rsp(struct a2mp_conn *a2mp_conn, struct a2mp_cmd_hdr *cmd, u8 *data)
+{
+       BT_DBG("change rsp");
+       return 0;
+}
+
+static int amp_info_req(struct a2mp_conn *a2mp_conn, struct a2mp_cmd_hdr *cmd, u8 *data)
+{
+       struct a2mp_info_req *req = (struct a2mp_info_req *)data;
+       struct cmd_context *ctx;
+       struct amp_controller *amp_contr;
+
+       BT_DBG("ctrl id 0x%4.4x", req->ctrl_id);
+
+       amp_contr = amp_get_contr_by_id(req->ctrl_id);
+
+       if (!amp_contr) {
+               struct l2cap_a2mp_packet *send;
+               struct a2mp_info_rsp rsp;
+
+               rsp.ctrl_id = req->ctrl_id;
+               rsp.status = AMP_STATUS_INVALID_CONTROLLER_ID;
+
+               send = amp_build_cmd(a2mp_conn, AMP_INFO_RSP, cmd->ident,
+                                       sizeof(struct a2mp_info_rsp), &rsp);
+               l2cap_a2mp_send_cmd(a2mp_conn->conn, send);
+               kfree(send);
+               goto exit;
+       }
+       ctx = kzalloc(sizeof(struct cmd_context), GFP_ATOMIC);
+       if (!ctx) {
+               BT_ERR("mm alloc fail in amp_info_req");
+               return 0;
+       }
+
+       ctx->type = PENDING_REMOTE_SIGNAL;
+       ctx->code = AMP_INFO_REQ;
+       ctx->identify = cmd->ident;
+       ctx->cmd = HCI_OP_READ_LOCAL_COMMANDS;
+       ctx->a2mp_conn = a2mp_conn;
+       amp_ctx_list_link(&amp_contr->context_q, ctx);
+       hci_read_local_amp_info(amp_contr->dev);
+exit:
+       return 0;
+}
+
+static int amp_info_rsp(struct a2mp_conn *a2mp_conn, struct a2mp_cmd_hdr *cmd, u8 *data)
+{
+       struct a2mp_info_rsp *rsp = (struct a2mp_info_rsp *)data;
+       struct controller_array *remote = a2mp_conn->remote;
+       struct amp_phy_link *phy_link;
+       u8 i;
+
+       BT_DBG("ctrl id 0x%x, rsp status 0x%x", rsp->ctrl_id, rsp->status);
+
+       phy_link = amp_get_phylink_by_iden(a2mp_conn, cmd->ident);
+       if (!phy_link) {
+               BT_DBG("can't find match phylink ");
+               return 0;
+       }
+
+       amp_clear_a2mp_timer(phy_link);
+
+       switch (rsp->status) {
+       case AMP_STATUS_OK:
+               for (i = 0; i < remote->size; i++) {
+                       if (remote->contr[i].id == rsp->ctrl_id) {
+                               remote->contr[i].total_bandwidth = __le32_to_cpu(rsp->total_bandwidth);
+                               remote->contr[i].max_bandwidth = __le32_to_cpu(rsp->max_bandwidth);
+                               remote->contr[i].min_latency = __le32_to_cpu(rsp->min_latency);
+                               remote->contr[i].pal_capability = __le16_to_cpu(rsp->pal_capability);
+                               remote->contr[i].amp_assoc_size = __le16_to_cpu(rsp->amp_assoc_size);
+                               break;
+                       }
+               }
+               if (phy_link->state == PHY_LINK_INFO_REQ_SENT) {
+                       struct a2mp_assoc_req req;
+                       struct l2cap_a2mp_packet *send;
+
+                       phy_link->identify = amp_get_ident(a2mp_conn);
+                       req.ctrl_id = rsp->ctrl_id;
+                       send = amp_build_cmd(a2mp_conn, AMP_ASSOC_REQ, phy_link->identify,
+                               sizeof(struct a2mp_assoc_req), &req);
+                       if (!send)
+                               return 0;
+                       l2cap_a2mp_send_cmd(a2mp_conn->conn, send);
+                       kfree(send);
+                       amp_set_a2mp_timer(phy_link, AMP_RESPONSE_TIMEOUT);
+                       phy_link->state = PHY_LINK_ASSOC_REQ_SENT;
+               }
+               break;
+       case AMP_STATUS_INVALID_CONTROLLER_ID:
+               amp_disc_phy_link(a2mp_conn, phy_link);
+               break;
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+static int amp_assoc_req(struct a2mp_conn *a2mp_conn, struct a2mp_cmd_hdr *cmd, u8 *data)
+{
+       struct a2mp_assoc_req *req = (struct a2mp_assoc_req *)data;
+       struct cmd_context *ctx;
+       struct amp_controller *amp_contr;
+
+       BT_DBG("ctrl id 0x%4.4x", req->ctrl_id);
+
+       amp_contr = amp_get_contr_by_id(req->ctrl_id);
+
+       if (!amp_contr) {
+               struct l2cap_a2mp_packet *send;
+               struct a2mp_assoc_rsp rsp;
+
+               rsp.ctrl_id = req->ctrl_id;
+               rsp.status = AMP_STATUS_INVALID_CONTROLLER_ID;
+
+               send = amp_build_cmd(a2mp_conn, AMP_ASSOC_RSP, cmd->ident,
+                                       sizeof(struct a2mp_assoc_rsp), &rsp);
+               l2cap_a2mp_send_cmd(a2mp_conn->conn, send);
+               kfree(send);
+               goto exit;
+       }
+
+       ctx = kzalloc(sizeof(struct cmd_context), GFP_ATOMIC);
+       if (!ctx) {
+               BT_ERR("mm alloc fail in amp_assoc_req");
+               return 0;
+       }
+
+       ctx->type = PENDING_REMOTE_SIGNAL;
+       ctx->code = AMP_ASSOC_REQ;
+       ctx->identify = cmd->ident;
+       ctx->cmd = HCI_OP_READ_LOCAL_AMP_ASSOC;
+       ctx->a2mp_conn = a2mp_conn;
+       amp_ctx_list_link(&amp_contr->context_q, ctx);
+       hci_read_local_amp_assoc(amp_contr->dev);
+exit:
+       return 0;
+}
+
+static int amp_assoc_rsp(struct a2mp_conn *a2mp_conn, struct a2mp_cmd_hdr *cmd, u8 *data)
+{
+       struct a2mp_assoc_rsp *rsp = (struct a2mp_assoc_rsp *)data;
+       struct amp_phy_link *phy_link;
+
+       BT_DBG("ctrl id 0x%x, rsp status 0x%x", rsp->ctrl_id, rsp->status);
+       phy_link = amp_get_phylink_by_iden(a2mp_conn, cmd->ident);
+       if (!phy_link) {
+               BT_DBG("can't find match phylink ");
+               return 0;
+       }
+       amp_clear_a2mp_timer(phy_link);
+       switch (rsp->status) {
+       case AMP_STATUS_OK:
+               if (phy_link->state == PHY_LINK_ASSOC_REQ_SENT) {
+                       struct amp_controller *amp_contr = amp_get_contr_by_id(phy_link->local_ctrl_id);
+                       struct cmd_context *ctx = kzalloc(sizeof(struct cmd_context), GFP_ATOMIC);
+                       struct amp_link_key dedicated_key = {.key_len = HCI_MAX_AMP_KEY_SIZE};
+                       struct hci_conn *hcon = a2mp_conn->conn->hcon;
+                       __u8 amp_type = amp_contr->dev->dev_type;
+
+                       phy_link->handle = amp_get_phy_handle();
+                       if (!ctx) {
+                               BT_ERR("null pointer in amp_assoc_rsp");
+                               return 0;
+                       }
+                       ctx->type = PENDING_REMOTE_SIGNAL;
+                       ctx->code = AMP_ASSOC_RSP;
+                       ctx->identify = cmd->ident;
+                       ctx->cmd = HCI_OP_CREATE_PHYSICAL_LINK;
+                       ctx->a2mp_conn = a2mp_conn;
+                       ctx->handle = phy_link->handle;
+                       amp_ctx_list_link(&amp_contr->context_q, ctx);
+
+                       if (!a2mp_conn->dedicated_lk[amp_type-1].valid) {
+
+                               if (!(a2mp_conn->status & GAMP_LK_VALID)) {
+                                       amp_create_generic_link_key(hcon->link_key,
+                                                               a2mp_conn->gamp_lk);
+                                       a2mp_conn->status |= GAMP_LK_VALID;
+                               }
+                               dedicated_key.key_type = hcon->key_type;
+                               amp_create_dedicated_link_key(a2mp_conn->gamp_lk,
+                                       hcon->key_type, dedicated_key.key);
+                               a2mp_conn->status |= REGENERATE_GAMP_LK;
+                               a2mp_conn->dedicated_lk[amp_type-1].key = dedicated_key;
+                       } else {
+                               dedicated_key = a2mp_conn->dedicated_lk[amp_type-1].key;
+                       }
+
+                       phy_link->hci_link = hci_phylink_create(amp_contr->dev, phy_link->handle,
+                                               &dedicated_key, rsp->assoc, (__le16_to_cpu(cmd->len) - 2),
+                                               hcon);
+                       phy_link->state = PHY_LINK_HCI_CREATE_SENT;
+               }
+               break;
+       case AMP_STATUS_INVALID_CONTROLLER_ID:
+               amp_disc_phy_link(a2mp_conn, phy_link);
+               break;
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+static int amp_create_phy_link_req(struct a2mp_conn *a2mp_conn, struct a2mp_cmd_hdr *cmd, u8 *data)
+{
+       struct a2mp_phy_link_req *req = (struct a2mp_phy_link_req *)data;
+       struct amp_link_key dedicated_key = {.key_len = HCI_MAX_AMP_KEY_SIZE};
+       struct hci_conn *hcon = a2mp_conn->conn->hcon;
+       struct cmd_context *ctx;
+       struct amp_phy_link *phy_link = NULL;
+       struct amp_controller *amp_contr;
+       struct l2cap_a2mp_packet *send;
+       struct a2mp_phy_link_rsp rsp;
+       __u8 amp_type;
+
+       BT_DBG("remote id 0x%4.4x,local ctrl id 0x%4.4x",
+                       req->local_ctrl_id, req->remote_ctrl_id);
+
+       amp_contr = amp_get_contr_by_id(req->remote_ctrl_id);
+       amp_type = amp_contr->dev->dev_type;
+
+       if (!amp_contr) {
+               rsp.local_ctrl_id = req->remote_ctrl_id;
+               rsp.remote_ctrl_id = req->local_ctrl_id;
+               rsp.status = AMP_STATUS_INVALID_CONTROLLER_ID;
+
+               send = amp_build_cmd(a2mp_conn, AMP_CREATE_PHY_LINK_RSP, cmd->ident,
+                                       sizeof(struct a2mp_phy_link_rsp), &rsp);
+               l2cap_a2mp_send_cmd(a2mp_conn->conn, send);
+               kfree(send);
+               goto exit;
+       }
+
+       /* collision resolve */
+       if (a2mp_conn->amp_role == AMP_ROLE_CREATOR) {
+               if (amp_collision_resolution(a2mp_conn->conn->src, a2mp_conn->conn->dst)) {
+                       BT_DBG("collison resolve to be winner");
+                       /* winner to be creator, send collision occured phy link rsp to remote*/
+                       rsp.local_ctrl_id = req->remote_ctrl_id;
+                       rsp.remote_ctrl_id = req->local_ctrl_id;
+                       rsp.status = AMP_STATUS_COLLISION_OCCURRED;
+
+                       send = amp_build_cmd(a2mp_conn, AMP_CREATE_PHY_LINK_RSP,
+                                               cmd->ident, sizeof(struct a2mp_phy_link_rsp), &rsp);
+                       l2cap_a2mp_send_cmd(a2mp_conn->conn, send);
+                       kfree(send);
+                       /* no need to progress*/
+                       goto exit;
+               } else {
+                       /* here lose to be creator, need exit previous creating progress*/
+                       /*only exist one phy link in creator progress*/
+                       BT_DBG("collison resolve to be loser");
+                       phy_link = amp_get_creator_progress_phy_link(a2mp_conn);
+                       if (phy_link) {
+                               a2mp_conn->amp_role = AMP_ROLE_RESPONDER;
+                               amp_disc_phy_link(a2mp_conn, phy_link);
+                       }
+               }
+       }
+
+       /*normal flow, respondor receive phy link req, send hci_accept*/
+       phy_link = kzalloc(sizeof(struct amp_phy_link), GFP_ATOMIC);
+       if (!phy_link)
+               return 0;
+       phy_link->local_ctrl_id = req->remote_ctrl_id;
+       phy_link->remote_ctrl_id = req->local_ctrl_id;
+       phy_link->handle = amp_get_phy_handle();
+       phy_link->a2mp_conn = a2mp_conn;
+       phy_link->local = amp_contr;
+
+       amp_a2mp_phylink_link(&a2mp_conn->phy_link_list, phy_link);
+       amp_contr_phylink_link(&amp_contr->phy_link_list, phy_link);
+
+       setup_timer(&phy_link->a2mp_timer, amp_a2mp_timeout,
+                                               (unsigned long) phy_link);
+       setup_timer(&phy_link->timer, amp_phylink_timeout,
+                                               (unsigned long) phy_link);
+
+       amp_set_phylink_timer(phy_link, AMP_PHY_LINK_TIMEOUT);
+
+       ctx = kzalloc(sizeof(struct cmd_context), GFP_ATOMIC);
+       if (!ctx)
+               return 0;
+       ctx->type = PENDING_REMOTE_SIGNAL;
+       ctx->code = AMP_CREATE_PHY_LINK_REQ;
+       ctx->identify = cmd->ident;
+       ctx->cmd = HCI_OP_CREATE_PHYSICAL_LINK;
+       ctx->a2mp_conn = a2mp_conn;
+       ctx->handle = phy_link->handle;
+       amp_ctx_list_link(&amp_contr->context_q, ctx);
+
+       if (!a2mp_conn->dedicated_lk[amp_type-1].valid) {
+
+               if (!(a2mp_conn->status & GAMP_LK_VALID)) {
+                       amp_create_generic_link_key(hcon->link_key,
+                                               a2mp_conn->gamp_lk);
+                       a2mp_conn->status |= GAMP_LK_VALID;
+               }
+               dedicated_key.key_type = hcon->key_type;
+               amp_create_dedicated_link_key(a2mp_conn->gamp_lk,
+                       hcon->key_type, dedicated_key.key);
+               a2mp_conn->status |= REGENERATE_GAMP_LK;
+               a2mp_conn->dedicated_lk[amp_type-1].key = dedicated_key;
+       } else {
+               dedicated_key = a2mp_conn->dedicated_lk[amp_type-1].key;
+       }
+
+       phy_link->hci_link = hci_phylink_accept(amp_contr->dev, phy_link->handle, &dedicated_key,
+                                               req->assoc, (__le16_to_cpu(cmd->len) - 2), hcon);
+       phy_link->state = PHY_LINK_HCI_ACCEPT_SENT;
+
+exit:
+       return 0;
+}
+
+static int amp_create_phy_link_rsp(struct a2mp_conn *a2mp_conn, struct a2mp_cmd_hdr *cmd, u8 *data)
+{
+       struct a2mp_phy_link_rsp *rsp = (struct a2mp_phy_link_rsp *)data;
+       struct amp_phy_link *phy_link;
+
+       BT_DBG("local ctrl id 0x%4.4x,remote id 0x%4.4x, status 0x%4.4x",
+                       rsp->local_ctrl_id, rsp->remote_ctrl_id, rsp->status);
+
+       phy_link = amp_get_phylink_by_iden(a2mp_conn, cmd->ident);
+
+       if (!phy_link) {
+               BT_DBG("can't find match phylink ");
+               return 0;
+       }
+
+       amp_clear_a2mp_timer(phy_link);
+
+       switch (rsp->status) {
+       case AMP_STATUS_OK:
+               if (phy_link->state == PHY_LINK_CREATE_REQ_SENT) {
+                       if (phy_link->flag == PHY_LINK_MAC_READY) {
+                               phy_link->state = PHY_LINK_CONNECTED;
+                               a2mp_conn->amp_role = AMP_ROLE_RESPONDER;
+                               amp_clear_phylink_timer(phy_link);
+                               atomic_dec(&a2mp_conn->req_cnt);
+                               l2cap_phylink_update(a2mp_conn->conn,
+                                       L2CAP_PHYLINK_READY, phy_link->hci_link);
+                       } else {
+                               phy_link->state = PHY_LINK_CREATE_RSP_RECV;
+                       }
+               }
+               break;
+       case AMP_STATUS_INVALID_CONTROLLER_ID:
+       case AMP_STATUS_UNABLE_TO_CREATE_LINK:
+       case AMP_STATUS_COLLISION_OCCURRED:
+       case AMP_STATUS_DISC_PACKET_RECV:
+       case AMP_STATUS_PHYSICAL_LINK_EXIST:
+       case AMP_STATUS_INSUFFICIENT_SECURITY:
+               a2mp_conn->amp_role = AMP_ROLE_RESPONDER;
+               amp_disc_phy_link(a2mp_conn, phy_link);
+               break;
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+static int amp_disc_phy_link_req(struct a2mp_conn *a2mp_conn, struct a2mp_cmd_hdr *cmd, u8 *data)
+{
+       struct a2mp_disc_phy_link_req *req = (struct a2mp_disc_phy_link_req *)data;
+       struct amp_phy_link *phy_link = NULL;
+       struct l2cap_a2mp_packet *send;
+       struct cmd_context *ctx;
+       struct a2mp_disc_phy_link_rsp rsp;
+
+       BT_DBG("remote ctrl id 0x%4.4x,local ctrl id 0x%4.4x", req->local_ctrl_id, req->remote_ctrl_id);
+
+       phy_link = amp_get_phylink_by_ctrlid(a2mp_conn, req->remote_ctrl_id, req->local_ctrl_id);
+       if (!phy_link) {
+               BT_DBG("can't disc inexist phy link");
+               rsp.local_ctrl_id = req->remote_ctrl_id;
+               rsp.remote_ctrl_id = req->local_ctrl_id;
+               rsp.status = AMP_STATUS_NO_PHYSICAL_LINK;
+               send = amp_build_cmd(a2mp_conn, AMP_DISC_PHY_LINK_RSP, cmd->ident,
+                                       sizeof(struct a2mp_disc_phy_link_rsp), &rsp);
+               l2cap_a2mp_send_cmd(a2mp_conn->conn, send);
+               kfree(send);
+               goto exit;
+       } else {
+               /* receive a create phy_link req, not send rsp yet, here can send a create phy_link rsp
+                * with "Failed - AMP Disconnected Physical Link Request packet received"
+                */
+               struct amp_controller *amp_contr = phy_link->local;
+
+               if (phy_link->state == PHY_LINK_HCI_ACCEPT_SENT) {
+                       struct list_head *p, *n;
+
+                       /* remove queue AMP_CREATE_PHY_LINK_REQ ctx, and send create phy link rsp, with fail*/
+                       write_lock(&amp_contr->context_q.lock);
+                       list_for_each_safe(p, n, &amp_contr->context_q.head) {
+                               ctx = list_entry(p, struct cmd_context, list);
+                               if ((ctx->code == AMP_CREATE_PHY_LINK_REQ)
+                                       && (ctx->handle == phy_link->handle)) {
+                                       struct a2mp_phy_link_rsp rsp;
+
+                                       rsp.local_ctrl_id = phy_link->local_ctrl_id;
+                                       rsp.remote_ctrl_id = phy_link->remote_ctrl_id;
+                                       rsp.status = AMP_STATUS_DISC_PACKET_RECV;
+
+                                       send = amp_build_cmd(ctx->a2mp_conn, AMP_CREATE_PHY_LINK_RSP,
+                                                       ctx->identify, sizeof(struct a2mp_phy_link_rsp), &rsp);
+                                       l2cap_a2mp_send_cmd(ctx->a2mp_conn->conn, send);
+                                       phy_link->state = PHY_LINK_CREATE_RSP_SENT;
+                                       kfree(send);
+
+                                       list_del(&ctx->list);
+                                       kfree(ctx);
+                               }
+                       }
+                       write_unlock(&amp_contr->context_q.lock);
+               }
+
+               /*send disc phy link rsp here*/
+               rsp.local_ctrl_id = phy_link->local_ctrl_id;
+               rsp.remote_ctrl_id = phy_link->remote_ctrl_id;
+               rsp.status = AMP_STATUS_OK;
+
+               send = amp_build_cmd(a2mp_conn, AMP_DISC_PHY_LINK_RSP, cmd->ident,
+                                       sizeof(struct a2mp_disc_phy_link_rsp), &rsp);
+               l2cap_a2mp_send_cmd(a2mp_conn->conn, send);
+               kfree(send);
+
+               /* disc phy link*/
+               l2cap_phylink_update(a2mp_conn->conn, L2CAP_PHYLINK_NOAVL, phy_link->hci_link);
+               amp_disc_phy_link(a2mp_conn, phy_link);
+       }
+exit:
+       return 0;
+}
+
+static int amp_disc_phy_link_rsp(struct a2mp_conn *a2mp_conn, struct a2mp_cmd_hdr *cmd, u8 *data)
+{
+       struct a2mp_disc_phy_link_rsp *rsp = (struct a2mp_disc_phy_link_rsp *)data;
+       struct amp_phy_link *phy_link;
+
+       BT_DBG("ctrl id 0x%4.4x,id 0x%4.4x, rsp status 0x%4.4x",
+               rsp->local_ctrl_id, rsp->remote_ctrl_id, rsp->status);
+
+       phy_link = amp_get_phylink_by_iden(a2mp_conn, cmd->ident);
+       if (!phy_link) {
+               BT_DBG("can't find match phylink ");
+               return 0;
+       }
+       amp_clear_a2mp_timer(phy_link);
+       switch (rsp->status) {
+       case AMP_STATUS_OK:
+               if (phy_link->state == PHY_LINK_DISC_REQ_SENT)
+                       phy_link->state = PHY_LINK_DISC_RSP_RECV;
+               break;
+       case AMP_STATUS_INVALID_CONTROLLER_ID:
+       case AMP_STATUS_NO_PHYSICAL_LINK:
+               amp_disc_phy_link(a2mp_conn, phy_link);
+               break;
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+void  amp_a2mp_process(struct l2cap_conn *conn, struct sk_buff *skb)
+{
+       struct a2mp_conn *a2mp_conn = (struct a2mp_conn *)conn->amp_handle;
+       struct a2mp_cmd_hdr cmd;
+       u8 *data = skb->data;
+       int len = skb->len;
+       int err = 0;
+
+       while (len >= AMP_CMD_HDR_LENGTH) {
+               u16 cmd_len;
+               memcpy(&cmd, data, AMP_CMD_HDR_LENGTH);
+               data += AMP_CMD_HDR_LENGTH;
+               len  -= AMP_CMD_HDR_LENGTH;
+
+               cmd_len = le16_to_cpu(cmd.len);
+
+               BT_DBG("code 0x%2.2x len %d id 0x%2.2x", cmd.code, cmd_len, cmd.ident);
+
+               if (cmd_len > len || !cmd.ident) {
+                       BT_DBG("corrupted command");
+                       break;
+               }
+
+               switch (cmd.code) {
+               case AMP_COMMAND_REJ:
+                       amp_command_rej(a2mp_conn, &cmd, data);
+                       break;
+
+               case AMP_DISCOVER_REQ:
+                       err = amp_discover_req(a2mp_conn, &cmd, data);
+                       break;
+
+               case AMP_DISCOVER_RSP:
+                       err = amp_discover_rsp(a2mp_conn, &cmd, data);
+                       break;
+
+               case AMP_CHANGE_NOTIFY:
+                       err = amp_change_notify(a2mp_conn, &cmd, data);
+                       break;
+
+               case AMP_CHANGE_RSP:
+                       err = amp_change_rsp(a2mp_conn, &cmd, data);
+                       break;
+
+               case AMP_INFO_REQ:
+                       err = amp_info_req(a2mp_conn, &cmd, data);
+                       break;
+
+               case AMP_INFO_RSP:
+                       err = amp_info_rsp(a2mp_conn, &cmd, data);
+                       break;
+
+               case AMP_ASSOC_REQ:
+                       err = amp_assoc_req(a2mp_conn, &cmd, data);
+                       break;
+
+               case AMP_ASSOC_RSP:
+                       err = amp_assoc_rsp(a2mp_conn, &cmd, data);
+                       break;
+
+               case AMP_CREATE_PHY_LINK_REQ:
+                       err = amp_create_phy_link_req(a2mp_conn, &cmd, data);
+                       break;
+
+               case AMP_CREATE_PHY_LINK_RSP:
+                       err = amp_create_phy_link_rsp(a2mp_conn, &cmd, data);
+                       break;
+
+               case AMP_DISC_PHY_LINK_REQ:
+                       err = amp_disc_phy_link_req(a2mp_conn, &cmd, data);
+                       break;
+
+               case AMP_DISC_PHY_LINK_RSP:
+                       err = amp_disc_phy_link_rsp(a2mp_conn, &cmd, data);
+                       break;
+
+               default:
+                       BT_ERR("Unknown signaling command 0x%2.2x", cmd.code);
+                       err = -EINVAL;
+                       break;
+               }
+
+               if (err) {
+                       struct l2cap_a2mp_packet *send;
+                       struct a2mp_cmd_rej rej;
+
+                       rej.reason = cpu_to_le16(0);
+                       send = amp_build_cmd(a2mp_conn, AMP_COMMAND_REJ, cmd.ident, sizeof(struct a2mp_cmd_rej), &rej);
+                       l2cap_a2mp_send_cmd(a2mp_conn->conn, send);
+                       kfree(send);
+               }
+
+               data += cmd_len;
+               len  -= cmd_len;
+       }
+
+       kfree_skb(skb);
+
+       return;
+}
--
1.6.3.3


^ permalink raw reply related

* [PATCH 1/3] Add BT3 AMP device support, by Atheros Linux BT3 team.
From: Dan Tian @ 2010-07-15  7:37 UTC (permalink / raw)
  To: linux-bluetooth@vger.kernel.org, Haijun Liu, Luis Rodriguez

>From 40cbfbf5c1bc9109364970956d9b6f74f5846c70 Mon Sep 17 00:00:00 2001
From: Haijun.Liu <Haijun.Liu@Atheros.com>
Date: Wed, 14 Jul 2010 22:50:56 +0800
Subject: [PATCH 1/3] Add BT3 AMP device support, by Atheros Linux BT3 team.


Signed-off-by: Haijun.Liu <Haijun.Liu@Atheros.com>
---
 include/net/bluetooth/hci.h      |  342 +++++++++++++++++++-
 include/net/bluetooth/hci_core.h |  264 ++++++++++++++-
 net/bluetooth/cmtp/core.c        |    1 +
 net/bluetooth/hci_conn.c         |  201 +++++++++++-
 net/bluetooth/hci_core.c         |  349 ++++++++++++++++++--
 net/bluetooth/hci_event.c        |  698 +++++++++++++++++++++++++++++++++++++-
 6 files changed, 1822 insertions(+), 33 deletions(-)

diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index ca2518e..702cef5 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -30,6 +30,12 @@
 #define HCI_MAX_EVENT_SIZE     260
 #define HCI_MAX_FRAME_SIZE     (HCI_MAX_ACL_SIZE + 4)

+#define HCI_MAX_AMP_KEY_SIZE   32
+/* Max80211AMPASSOCLen p1763. */
+#define HCI_MAX_AMP_ASSOC_SIZE 672
+
+#define HCI_MAX_AMP_ASSOC_FRAGMENT     248
+
 /* HCI dev events */
 #define HCI_DEV_REG                    1
 #define HCI_DEV_UNREG                  2
@@ -152,6 +158,7 @@ enum {
 /* ACL flags */
 #define ACL_CONT               0x01
 #define ACL_START              0x02
+#define ACL_COMPLETE   0x03
 #define ACL_ACTIVE_BCAST       0x04
 #define ACL_PICO_BCAST         0x08

@@ -281,6 +288,11 @@ struct hci_cp_link_key_reply {
        __u8     link_key[16];
 } __attribute__ ((packed));

+struct hci_rp_link_key_reply {
+       __u8    status;
+       bdaddr_t        bdaddr;
+} __attribute__ ((packed));
+
 #define HCI_OP_LINK_KEY_NEG_REPLY      0x040c
 struct hci_cp_link_key_neg_reply {
        bdaddr_t bdaddr;
@@ -377,6 +389,75 @@ struct hci_cp_reject_sync_conn_req {
        __u8     reason;
 } __attribute__ ((packed));

+#define HCI_OP_CREATE_PHYSICAL_LINK    0x0435
+struct hci_cp_create_physical_link {
+       __u8    handle;
+       __u8    key_len;
+       __u8    key_type;
+       __u8    key[HCI_MAX_AMP_KEY_SIZE];
+} __attribute__ ((packed));
+
+#define HCI_OP_ACCEPT_PHYSICAL_LINK    0x0436
+struct hci_cp_accept_physical_link {
+       __u8    handle;
+       __u8    key_len;
+       __u8    key_type;
+       __u8    key[HCI_MAX_AMP_KEY_SIZE];
+} __attribute__ ((packed));
+
+#define HCI_OP_DISCONN_PHYSICAL_LINK   0x0437
+struct hci_cp_disconn_physical_link {
+       __u8    handle;
+       __u8    reason;
+} __attribute__ ((packed));
+
+struct hci_ext_flow_spec_le {
+       __u8    id;
+       __u8    service_type;
+       __le16  max_sdu_size;
+       __le32  sdu_inter_time;
+       __le32  access_latency;
+       __le32  flush_timeout;
+} __attribute__ ((packed));
+
+#define HCI_OP_CREATE_LOGICAL_LINK     0x0438
+struct hci_cp_create_logical_link {
+       __u8    handle;
+       struct hci_ext_flow_spec_le     tx_flow_spec;
+       struct hci_ext_flow_spec_le     rx_flow_spec;
+} __attribute__ ((packed));
+
+#define HCI_OP_ACCEPT_LOGICAL_LINK     0x0439
+struct hci_cp_accept_logical_link {
+       __u8    handle;
+       struct hci_ext_flow_spec_le     tx_flow_spec;
+       struct hci_ext_flow_spec_le     rx_flow_spec;
+} __attribute__ ((packed));
+
+#define HCI_OP_DISCONN_LOGICAL_LINK    0x043a
+struct hci_cp_disconn_logical_link {
+       __le16  handle;
+} __attribute__ ((packed));
+
+#define HCI_OP_LOGICAL_LINK_CANCEL     0x043b
+struct hci_cp_logical_link_cancel {
+       __u8    handle;
+       __u8    tx_flow_spec_id;
+} __attribute__ ((packed));
+
+struct hci_rp_logical_link_cancel {
+       __u8    status;
+       __u8    handle;
+       __u8    tx_flow_spec_id;
+} __attribute__ ((packed));
+
+#define HCI_OP_FLOW_SPEC_MODIFY                0x043c
+struct hci_cp_flow_spec_modify {
+       __le16  handle;
+       struct hci_ext_flow_spec_le     tx_flow_spec;
+       struct hci_ext_flow_spec_le     rx_flow_spec;
+} __attribute__ ((packed));
+
 #define HCI_OP_SNIFF_MODE              0x0803
 struct hci_cp_sniff_mode {
        __le16   handle;
@@ -489,7 +570,7 @@ struct hci_rp_read_local_name {

 #define HCI_OP_WRITE_PG_TIMEOUT                0x0c18

-#define HCI_OP_WRITE_SCAN_ENABLE       0x0c1a
+#define HCI_OP_WRITE_SCAN_ENABLE       0x0c1a
        #define SCAN_DISABLED           0x00
        #define SCAN_INQUIRY            0x01
        #define SCAN_PAGE               0x02
@@ -548,6 +629,107 @@ struct hci_cp_write_ssp_mode {
        __u8     mode;
 } __attribute__ ((packed));

+#define HCI_OP_READ_LOGICAL_LINK_ACCEPT_TIMEOUT        0x0c61
+struct hci_rp_read_logical_link_accept_timeout {
+       __u8    status;
+       __le16  timeout;
+} __attribute__ ((packed));
+
+#define HCI_OP_WRITE_LOGICAL_LINK_ACCEPT_TIMEOUT 0x0c62
+struct hci_cp_write_logical_link_accept_timeout {
+       __le16  timeout;
+} __attribute__ ((packed));
+
+struct hci_rp_write_logical_link_accept_timeout {
+       __u8    status;
+} __attribute__ ((packed));
+
+#define HCI_OP_SET_EVENT_MASK_PAGE2    0x0c63
+struct hci_cp_set_event_mask_page2 {
+       __u8    evt_mask_page2[8];
+} __attribute__ ((packed));
+
+struct hci_rp_set_event_mask_page2 {
+       __u8    status;
+} __attribute__ ((packed));
+
+struct location_data {
+       __u8    domain_aware;
+       __u8    domain;
+       __u8    domain_option;
+       __u8    option;
+} __attribute__ ((packed));
+
+#define HCI_OP_READ_LOCATION_DATA      0x0c64
+struct hci_rp_read_location_data {
+       __u8    status;
+       struct location_data location;
+} __attribute__ ((packed));
+
+#define HCI_OP_WRITE_LOCATION_DATA     0x0c65
+struct hci_cp_write_location_data {
+       struct location_data location;
+} __attribute__ ((packed));
+
+#define HCI_OP_READ_FLOW_CONTROL_MODE  0x0c66
+struct hci_rp_read_flow_control_mode {
+       __u8    status;
+       __u8    mode;
+} __attribute__ ((packed));
+
+#define HCI_OP_WRITE_FLOW_CONTROL_MODE 0x0c67
+struct hci_cp_write_flow_control_mode {
+       __u8    mode;
+} __attribute__ ((packed));
+
+struct hci_rp_write_flow_control_mode {
+       __u8    status;
+} __attribute__ ((packed));
+
+#define HCI_OP_READ_ETX_POWER_LEVEL    0x0c68
+struct hci_cp_etx_power_level {
+       __le16  handle;
+       __u8    type;
+} __attribute__ ((packed));
+
+struct hci_rp_etx_power_level {
+       __u8    status;
+       __le16  handle;
+       __u8    gfsk;
+       __u8    dqpsk;
+       __u8    eight_dpsk;
+} __attribute__ ((packed));
+
+#define HCI_OP_READ_BEST_EFFORT_FLUSH_TIMEOUT  0x0c69
+struct hci_cp_read_best_effort_flush_timeout {
+       __le16  handle;
+} __attribute__ ((packed));
+
+struct hci_rp_read_best_effort_flush_timeout {
+       __u8    status;
+       __le32  timeout;
+} __attribute__ ((packed));
+
+#define HCI_OP_WRITE_BEST_EFFORT_FlUSH_TIMEOUT 0x0c6a
+struct hci_cp_write_best_effort_flush_timeout {
+       __le16  handle;
+       __le32  timeout;
+} __attribute__ ((packed));
+
+struct hci_rp_write_best_effort_flush_timeout {
+       __u8    status;
+} __attribute__ ((packed));
+
+#define HCI_OP_SHORT_RANGE_MODE                0x0c6b
+struct hci_cp_short_range_mode {
+       __u8    handle;
+       __u8    mode;
+} __attribute__ ((packed));
+
+struct hci_rp_short_range_mode {
+       __u8    status;
+} __attribute__ ((packed));
+
 #define HCI_OP_READ_LOCAL_VERSION      0x1001
 struct hci_rp_read_local_version {
        __u8     status;
@@ -593,6 +775,67 @@ struct hci_rp_read_bd_addr {
        bdaddr_t bdaddr;
 } __attribute__ ((packed));

+#define HCI_OP_READ_DATA_BLOCK_SIZE    0x100a
+struct hci_rp_read_data_block_size {
+       __u8    status;
+       __le16  max_pkt_len;
+       __le16  max_blk_len;
+       __le16  num_blks;
+} __attribute__ ((packed));
+
+#define HCI_OP_READ_ENCRYPT_KEY_SIZE   0x1408
+struct hci_cp_read_encrypt_key_size {
+       __le16  handle;
+} __attribute__ ((packed));
+
+struct hci_rp_read_encrypt_key_size {
+       __u8    status;
+       __le16  handle;
+       __u8    key_size;
+} __attribute__ ((packed));
+
+#define HCI_OP_READ_LOCAL_AMP_INFO     0x1409
+struct hci_rp_read_local_amp_info {
+       __u8    status;
+       __u8    amp_status;
+       __le32  total_bandwidth;
+       __le32  max_guaranteed_bandwidth;
+       __le32  min_latency;
+       __le32  max_pdu_size;
+       __u8    controller_type;
+       __le16  pal_caps;
+       __le16  max_amp_assoc_len;
+       __le32  max_flush_timeout;
+       __le32  best_effort_flush_timeout;
+} __attribute__ ((packed));
+
+#define HCI_OP_READ_LOCAL_AMP_ASSOC    0x140a
+struct hci_cp_read_local_amp_assoc {
+       __u8    handle;
+       __le16  len_so_far;
+       __le16  max_remote_amp_assoc_len;
+} __attribute__ ((packed));
+
+struct hci_rp_read_local_amp_assoc {
+       __u8    status;
+       __u8    handle;
+       __le16  amp_assoc_remaining_len;
+       __u8    amp_assoc_fragment[HCI_MAX_AMP_ASSOC_FRAGMENT];
+} __attribute__ ((packed));
+
+#define HCI_OP_WRITE_REMOTE_AMP_ASSOC  0x140b
+struct hci_cp_write_remote_amp_assoc {
+       __u8    handle;
+       __le16  len_so_far;
+       __le16  amp_assoc_remaining_len;
+       __u8    amp_assoc_fragment[HCI_MAX_AMP_ASSOC_FRAGMENT];
+} __attribute__ ((packed));
+
+struct hci_rp_write_remote_amp_assoc {
+       __u8    status;
+       __u8    handle;
+} __attribute__ ((packed));
+
 /* ---- HCI Events ---- */
 #define HCI_EV_INQUIRY_COMPLETE                0x01

@@ -698,6 +941,11 @@ struct hci_ev_cmd_status {
        __le16   opcode;
 } __attribute__ ((packed));

+#define HCI_EV_FLUSH_OCCURRED          0x11
+struct hci_ev_flush_occurred {
+       __u16   handle;
+} __attribute__ ((packed));
+
 #define HCI_EV_ROLE_CHANGE             0x12
 struct hci_ev_role_change {
        __u8     status;
@@ -845,6 +1093,98 @@ struct hci_ev_remote_host_features {
        __u8     features[8];
 } __attribute__ ((packed));

+#define HCI_EV_PHYSICAL_LINK_COMPLETE  0x40
+struct hci_ev_physical_link_complete {
+       __u8    status;
+       __u8    handle;
+} __attribute__ ((packed));
+
+#define HCI_EV_CHANNEL_SELECTED                        0x41
+struct hci_ev_channel_selected {
+       __u8    handle;
+} __attribute__ ((packed));
+
+#define HCI_EV_DISCONN_PHYSICAL_LINK_COMPLETE  0x42
+struct hci_ev_disconn_physical_link_complete {
+       __u8    status;
+       __u8    handle;
+       __u8    reason;
+} __attribute__ ((packed));
+
+#define HCI_EV_PHYSICAL_LINK_LOSS_EARLY_WARNING        0x43
+struct hci_ev_physical_link_loss_early_warning {
+       __u8    handle;
+       __u8    reason;
+} __attribute__ ((packed));
+
+#define HCI_EV_PHYSICAL_LINK_RECOVERY          0x44
+struct hci_ev_physical_link_recovery {
+       __u8    status;
+} __attribute__ ((packed));
+
+#define HCI_EV_LOGICAL_LINK_COMPLETE           0x45
+struct hci_ev_logical_link_complete {
+       __u8    status;
+       __le16  logical_link_handle;
+       __u8    physical_link_handle;
+       __u8    tx_flow_spec_id;
+} __attribute__ ((packed));
+
+#define HCI_EV_DISCONN_LOGICAL_LINK_COMPLETE   0x46
+struct hci_ev_disconn_logical_link_complete {
+       __u8    status;
+       __le16  handle;
+       __u8    reason;
+} __attribute__ ((packed));
+
+#define HCI_EV_FLOW_SPEC_MODIFY_COMPLETE       0x47
+struct hci_ev_flow_spec_modify_complete {
+       __u8    status;
+       __le16  handle;
+} __attribute__ ((packed));
+
+#define HCI_EV_NUM_OF_COMPLETED_DATA_BLOCKS    0x48
+struct hci_ev_num_of_completed_data_blocks {
+       __le16  num_data_blocks;
+       __u8    num_handles;
+} __attribute__ ((packed));
+
+#define HCI_EV_SHORT_RANGE_MODE_CHANGE_COMPLETE        0x4c
+struct hci_ev_short_range_mode_change_complete {
+       __u8    status;
+       __u8    handle;
+       __u8    state;
+} __attribute__ ((packed));
+
+#define HCI_EV_AMP_STATUS_CHANGE               0x4d
+struct hci_ev_amp_status_change {
+       __u8    status;
+       __u8    amp_status;
+} __attribute__ ((packed));
+
+#define HCI_EV_AMP_START_TEST                  0x49
+struct hci_ev_amp_start_test {
+       __u8    status;
+       __u8    scenario;
+} __attribute__ ((packed));
+
+#define HCI_EV_AMP_TEST_END                    0x4a
+struct hci_ev_amp_test_end {
+       __u8    status;
+       __u8    scenario;
+} __attribute__ ((packed));
+
+#define HCI_EV_AMP_RECEIVER_REPORT             0x4b
+struct hci_ev_amp_receiver_report {
+       __u8    controller_type;
+       __u8    reason;
+       __u32   evt_type;
+       __le16  num_frames;
+       __le16  num_error_frames;
+       __u32   num_bits;
+       __u32   num_error_bits;
+} __attribute__ ((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 600372d..04267a9 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -67,6 +67,40 @@ struct bdaddr_list {
        bdaddr_t bdaddr;
 };

+struct amp_assoc {
+       __u16   len;
+       __u16   offset;
+       __u8    data[HCI_MAX_AMP_ASSOC_SIZE];
+};
+
+struct amp_info {
+       __u8    amp_status;
+       __le32  total_bandwidth;
+       __le32  max_guaranteed_bandwidth;
+       __le32  min_latency;
+       __le32  max_pdu_size;
+       __u8    ctrl_type;
+       __le16  pal_caps;
+       __le16  max_assoc_len;
+       __le32  max_flush_to;
+       __le32  best_effort_flush_to;
+};
+
+struct amp_link_key {
+       __u8    key_len;
+       __u8    key_type;
+       __u8    key[HCI_MAX_AMP_KEY_SIZE];
+};
+
+struct ext_flow_spec {
+       __u8    id;
+       __u8    service_type;
+       __u16   max_sdu_size;
+       __u32   sdu_inter_time;
+       __u32   access_latency;
+       __u32   flush_timeout;
+} __attribute__ ((packed));
+
 struct hci_dev {
        struct list_head list;
        spinlock_t      lock;
@@ -142,6 +176,7 @@ struct hci_dev {
        void                    *core_data;

        atomic_t                promisc;
+       void                    *amp_controller;

        struct dentry           *debugfs;

@@ -152,6 +187,11 @@ struct hci_dev {

        struct module           *owner;

+       struct amp_info         ctrl_info;
+       __u8                    ctrl_id;
+       struct amp_assoc        local_assoc;
+       struct list_head        phy_links;
+
        int (*open)(struct hci_dev *hdev);
        int (*close)(struct hci_dev *hdev);
        int (*flush)(struct hci_dev *hdev);
@@ -161,6 +201,27 @@ struct hci_dev {
        int (*ioctl)(struct hci_dev *hdev, unsigned int cmd, unsigned long arg);
 };

+enum {
+       HCI_AMP_ROLE_INITIATOR,
+       HCI_AMP_ROLE_RESPONDER
+};
+struct hci_phy_link {
+       struct list_head list;
+
+       __u8            handle;
+       __u16           state;
+       void            *l2cap_data;
+       __u8            amp_role;
+
+       void            *priv_data;
+       __u16   acl_link_state;
+       struct list_head                log_links;
+       struct hci_dev          *hdev;
+       /* remote amp assoc data. */
+       struct amp_assoc        remote_assoc;
+
+};
+
 struct hci_conn {
        struct list_head list;

@@ -187,6 +248,9 @@ struct hci_conn {
        __u16            disc_timeout;
        unsigned long    pend;

+       __u8            link_key[16];
+       __u8            key_type;
+
        unsigned int     sent;

        struct sk_buff_head data_q;
@@ -205,6 +269,11 @@ struct hci_conn {
        void            *sco_data;
        void            *priv;

+       __u8    amp_role;
+       /* id value in Tx_Flow_Spec to identify the logical link. */
+       __u8    tx_flow_spec_id;
+       struct hci_phy_link     *phylink;
+
        struct hci_conn *link;
 };

@@ -215,8 +284,8 @@ extern rwlock_t hci_dev_list_lock;
 extern rwlock_t hci_cb_list_lock;

 /* ----- Inquiry cache ----- */
-#define INQUIRY_CACHE_AGE_MAX   (HZ*30)   // 30 seconds
-#define INQUIRY_ENTRY_AGE_MAX   (HZ*60)   // 60 seconds
+#define INQUIRY_CACHE_AGE_MAX   (HZ*30)   /* 30 seconds        */
+#define INQUIRY_ENTRY_AGE_MAX   (HZ*60)   /* 60 seconds        */

 #define inquiry_cache_lock(c)          spin_lock(&c->lock)
 #define inquiry_cache_unlock(c)                spin_unlock(&c->lock)
@@ -287,6 +356,9 @@ static inline void hci_conn_hash_del(struct hci_dev *hdev, struct hci_conn *c)
                h->sco_num--;
 }

+struct hci_conn *hci_loglink_lookup_handle
+(struct hci_dev *hdev, __u16 log_handle);
+
 static inline struct hci_conn *hci_conn_hash_lookup_handle(struct hci_dev *hdev,
                                        __u16 handle)
 {
@@ -294,10 +366,14 @@ static inline struct hci_conn *hci_conn_hash_lookup_handle(struct hci_dev *hdev,
        struct list_head *p;
        struct hci_conn  *c;

-       list_for_each(p, &h->list) {
-               c = list_entry(p, struct hci_conn, list);
-               if (c->handle == handle)
-                       return c;
+       if (hdev->dev_type == HCI_80211) {
+               return hci_loglink_lookup_handle(hdev, handle);
+       } else {
+               list_for_each(p, &h->list) {
+                       c = list_entry(p, struct hci_conn, list);
+                       if (c->handle == handle)
+                               return c;
+               }
        }
        return NULL;
 }
@@ -362,6 +438,7 @@ static inline void hci_conn_hold(struct hci_conn *conn)

 static inline void hci_conn_put(struct hci_conn *conn)
 {
+       BT_DBG("put conn:%p, refcnt:%d", conn, atomic_read(&conn->refcnt));
        if (atomic_dec_and_test(&conn->refcnt)) {
                unsigned long timeo;
                if (conn->type == ACL_LINK) {
@@ -469,6 +546,11 @@ struct hci_proto {
        int (*recv_acldata)     (struct hci_conn *conn, struct sk_buff *skb, __u16 flags);
        int (*recv_scodata)     (struct hci_conn *conn, struct sk_buff *skb);
        int (*security_cfm)     (struct hci_conn *conn, __u8 status, __u8 encrypt);
+
+       int (*loglink_create_cfm) (struct hci_conn *conn, __u8 status);
+       int (*loglink_accept_cfm) (struct hci_conn *conn, __u8 status);
+       int (*loglink_modify_cfm) (struct hci_conn *conn, __u8 status);
+       int (*loglink_put_cfm) (struct hci_conn *conn, __u8 status);
 };

 static inline int hci_proto_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 type)
@@ -561,6 +643,45 @@ static inline void hci_proto_encrypt_cfm(struct hci_conn *conn, __u8 status, __u
                hp->security_cfm(conn, status, encrypt);
 }

+static inline void hci_proto_loglink_create_cfm(struct hci_conn *conn,
+                                               __u8 status)
+{
+       register struct hci_proto *hp;
+
+       hp = hci_proto[HCI_PROTO_L2CAP];
+       if (hp && hp->loglink_create_cfm)
+               hp->loglink_create_cfm(conn, status);
+}
+
+static inline void hci_proto_loglink_accept_cfm(struct hci_conn *conn,
+                                               __u8 status)
+{
+       register struct hci_proto *hp;
+
+       hp = hci_proto[HCI_PROTO_L2CAP];
+       if (hp && hp->loglink_accept_cfm)
+               hp->loglink_accept_cfm(conn, status);
+}
+
+static inline void hci_proto_loglink_modify_cfm(struct hci_conn *conn,
+                                               __u8 status)
+{
+       register struct hci_proto *hp;
+
+       hp = hci_proto[HCI_PROTO_L2CAP];
+       if (hp && hp->loglink_modify_cfm)
+               hp->loglink_modify_cfm(conn, status);
+}
+
+static inline void hci_proto_loglink_put_cfm(struct hci_conn *conn, __u8 status)
+{
+       register struct hci_proto *hp;
+
+       hp = hci_proto[HCI_PROTO_L2CAP];
+       if (hp && hp->loglink_put_cfm)
+               hp->loglink_put_cfm(conn, status);
+}
+
 int hci_register_proto(struct hci_proto *hproto);
 int hci_unregister_proto(struct hci_proto *hproto);

@@ -573,6 +694,15 @@ struct hci_cb {
        void (*security_cfm)    (struct hci_conn *conn, __u8 status, __u8 encrypt);
        void (*key_change_cfm)  (struct hci_conn *conn, __u8 status);
        void (*role_switch_cfm) (struct hci_conn *conn, __u8 status, __u8 role);
+
+       /* ---- AMP HCI callbacks. ---- */
+       int (*create_physical_link_cfm) (struct hci_dev *hdev, __u8 handle,
+                                        __u8 status);
+       int (*put_physical_link_cfm) (struct hci_dev *hdev, __u8 handle,
+                                       __u8 status, __u8 reason);
+
+       int (*local_assoc_ind) (struct hci_dev *hdev, __u8 status);
+       int (*local_info_ind) (struct hci_dev *hdev, __u8 status);
 };

 static inline void hci_auth_cfm(struct hci_conn *conn, __u8 status)
@@ -640,6 +770,69 @@ static inline void hci_role_switch_cfm(struct hci_conn *conn, __u8 status, __u8
        read_unlock_bh(&hci_cb_list_lock);
 }

+static inline int hci_create_physical_link_cfm(struct hci_dev *hdev,
+                                               __u8 handle, __u8 status)
+{
+       struct list_head *p;
+
+       read_lock_bh(&hci_cb_list_lock);
+       list_for_each(p, &hci_cb_list) {
+               struct hci_cb *cb = list_entry(p, struct hci_cb, list);
+               if (cb->create_physical_link_cfm)
+                       cb->create_physical_link_cfm(hdev, handle, status);
+       }
+       read_unlock_bh(&hci_cb_list_lock);
+
+       return 0;
+}
+
+static inline int hci_put_physical_link_cfm(struct hci_dev *hdev, __u8 handle,
+                                               __u8 status, __u8 reason)
+{
+       struct list_head *p;
+
+       read_lock_bh(&hci_cb_list_lock);
+       list_for_each(p, &hci_cb_list) {
+               struct hci_cb *cb = list_entry(p, struct hci_cb, list);
+               if (cb->put_physical_link_cfm)
+                       cb->put_physical_link_cfm(hdev, handle, status, reason);
+       }
+       read_unlock_bh(&hci_cb_list_lock);
+
+       return 0;
+}
+
+static inline int hci_local_assoc_ind(struct hci_dev *hdev, __u8 status)
+{
+       struct list_head *p;
+
+       read_lock_bh(&hci_cb_list_lock);
+       list_for_each(p, &hci_cb_list) {
+               struct hci_cb *cb = list_entry(p, struct hci_cb, list);
+               if (cb->local_assoc_ind)
+                       cb->local_assoc_ind(hdev, status);
+       }
+       read_unlock_bh(&hci_cb_list_lock);
+
+       return 0;
+}
+
+static inline int hci_local_info_ind(struct hci_dev *hdev, __u8 status)
+{
+       struct list_head *p;
+
+       read_lock_bh(&hci_cb_list_lock);
+       list_for_each(p, &hci_cb_list) {
+               struct hci_cb *cb = list_entry(p, struct hci_cb, list);
+               if (cb->local_info_ind)
+                       cb->local_info_ind(hdev, status);
+       }
+       read_unlock_bh(&hci_cb_list_lock);
+
+       return 0;
+}
+
+
 int hci_register_cb(struct hci_cb *hcb);
 int hci_unregister_cb(struct hci_cb *hcb);

@@ -647,8 +840,8 @@ int hci_register_notifier(struct notifier_block *nb);
 int hci_unregister_notifier(struct notifier_block *nb);

 int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen, void *param);
-void hci_send_acl(struct hci_conn *conn, struct sk_buff *skb, __u16 flags);
-void hci_send_sco(struct hci_conn *conn, struct sk_buff *skb);
+int hci_send_acl(struct hci_conn *conn, struct sk_buff *skb, __u16 flags);
+int hci_send_sco(struct hci_conn *conn, struct sk_buff *skb);

 void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 opcode);

@@ -686,4 +879,59 @@ struct hci_sec_filter {

 void hci_req_complete(struct hci_dev *hdev, int result);

+
+
+/* ------ AMP HCI interface for upper layer ----- */
+#define HCI_CREATE_PHYLINK_PEND        0x40
+
+static inline struct hci_phy_link
+*hci_phylink_lookup_handle(struct hci_dev *hdev, __u8 phy_handle)
+{
+       struct hci_phy_link *plink;
+       struct list_head *p;
+
+       list_for_each(p, &hdev->phy_links) {
+               plink = list_entry(p, struct hci_phy_link, list);
+               if (plink->handle == phy_handle)
+                       return plink;
+       }
+       return NULL;
+}
+
+static inline void hci_loglink_hold(struct hci_conn *conn)
+{
+       atomic_inc(&conn->refcnt);
+}
+
+int hci_loglink_del(struct hci_conn *conn);
+
+void hci_loglink_put(struct hci_conn *conn);
+
+struct hci_conn
+*hci_loglink_lookup_tx_flow_spec_id(struct hci_phy_link *plink, __u8 id);
+
+int hci_phylink_del(struct hci_phy_link *plink);
+
+struct hci_phy_link *hci_phylink_create(struct hci_dev *hdev, __u8 phy_handle,
+                                               struct amp_link_key *key,
+                                               __u8 *rem_assoc,
+                                               __u16 assoc_size,
+                                               void *priv_data);
+struct hci_phy_link *hci_phylink_accept(struct hci_dev *hdev, __u8 phy_handle,
+                                               struct amp_link_key *key,
+                                               __u8 *rem_assoc,
+                                               __u16 assoc_size,
+                                               void *priv_data);
+void hci_phylink_put(struct hci_phy_link *phy_link, __u8 reason);
+
+struct hci_conn *hci_loglink_create(void *handle, struct ext_flow_spec *tx,
+                                       struct ext_flow_spec *rx);
+struct hci_conn *hci_loglink_accept(void *handle, struct ext_flow_spec *tx,
+                                       struct ext_flow_spec *rx);
+void hci_loglink_modify(struct hci_conn *hconn, struct ext_flow_spec *tx,
+                                       struct ext_flow_spec *rx);
+void hci_loglink_put(struct hci_conn *hconn);
+void hci_read_local_amp_info(struct hci_dev *hdev);
+void hci_read_local_amp_assoc(struct hci_dev *hdev);
+
 #endif /* __HCI_CORE_H */
diff --git a/net/bluetooth/cmtp/core.c b/net/bluetooth/cmtp/core.c
index d4c6af0..51c4b5c 100644
--- a/net/bluetooth/cmtp/core.c
+++ b/net/bluetooth/cmtp/core.c
@@ -40,6 +40,7 @@
 #include <linux/isdn/capilli.h>

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

 #include "cmtp.h"
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index e9fef83..c7f9134 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -161,7 +161,7 @@ static void hci_conn_timeout(unsigned long arg)
        struct hci_dev *hdev = conn->hdev;
        __u8 reason;

-       BT_DBG("conn %p state %d", conn, conn->state);
+       BT_DBG("conn %p state %d, refcnt:%d", conn, conn->state, atomic_read(&conn->refcnt));

        if (atomic_read(&conn->refcnt))
                return;
@@ -203,6 +203,7 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
        BT_DBG("%s dst %s", hdev->name, batostr(dst));

        conn = kzalloc(sizeof(struct hci_conn), GFP_ATOMIC);
+       BT_DBG("kzalloc hci_conn:%p", conn);
        if (!conn)
                return NULL;

@@ -708,3 +709,201 @@ int hci_get_auth_info(struct hci_dev *hdev, void __user *arg)

        return copy_to_user(arg, &req, sizeof(req)) ? -EFAULT : 0;
 }
+
+struct hci_conn *hci_loglink_lookup_handle(struct hci_dev *hdev, __u16 log_handle)
+{
+
+       struct hci_conn *conn;
+       struct list_head *p;
+       struct list_head *p2;
+       struct hci_phy_link *plink;
+
+       list_for_each(p, &hdev->phy_links) {
+               plink = list_entry(p, struct hci_phy_link, list);
+               list_for_each(p2, &plink->log_links) {
+                       conn = list_entry(p2, struct hci_conn, list);
+                       if (conn->handle == log_handle)
+                               return conn;
+               }
+       }
+       return NULL;
+
+}
+
+struct hci_conn *hci_loglink_lookup_tx_flow_spec_id(struct hci_phy_link *plink, __u8 id)
+{
+       struct hci_conn *conn;
+       struct list_head *p;
+
+       list_for_each(p, &plink->log_links) {
+               conn = list_entry(p, struct hci_conn, list);
+               if (conn->tx_flow_spec_id == id)
+                       return conn;
+       }
+       return NULL;
+}
+
+static struct hci_conn *hci_loglink_add(struct hci_phy_link *plink, __u8 id, int type, __u8 role)
+{
+       struct hci_conn *conn;
+       struct hci_dev *hdev = plink->hdev;
+
+       BT_DBG("phylink:%p", plink);
+
+       conn = kzalloc(sizeof(struct hci_conn), GFP_ATOMIC);
+       BT_DBG("kzalloc conn:%p", conn);
+       if (!conn)
+               return NULL;
+
+       conn->hdev = hdev;
+       conn->phylink = plink;
+       conn->type = type;
+       conn->tx_flow_spec_id = id;
+       conn->state = BT_OPEN;
+       conn->amp_role = role;
+
+       skb_queue_head_init(&conn->data_q);
+
+       list_add(&conn->list, &plink->log_links);
+
+       return conn;
+
+}
+
+int hci_loglink_del(struct hci_conn *conn)
+{
+       struct hci_dev *hdev = conn->hdev;
+       /*struct hci_phy_link *plink = conn->phylink;*/
+
+       BT_DBG("%s conn:%p handle %d", hdev->name, conn, conn->handle);
+
+       skb_queue_purge(&conn->data_q);
+
+       /*hci_phylink_put(plink, 0x13);*/
+       list_del(&conn->list);
+       kfree(conn);
+       return 0;
+}
+
+static inline void hci_build_ext_flow_spec(struct hci_ext_flow_spec_le *to, struct ext_flow_spec *from)
+{
+       to->id = from->id;
+       to->service_type = from->service_type;
+       to->max_sdu_size = cpu_to_le16(from->max_sdu_size);
+       to->sdu_inter_time = cpu_to_le32(from->sdu_inter_time);
+       to->access_latency = cpu_to_le32(from->access_latency);
+       to->flush_timeout = cpu_to_le32(from->flush_timeout);
+}
+
+static void hci_loglink_connect(struct hci_conn *conn, __u8 phy_handle, __u8 amp_role,
+                               struct ext_flow_spec *tx, struct ext_flow_spec *rx)
+{
+       struct hci_dev *hdev = conn->hdev;
+       struct hci_cp_create_logical_link cp;
+
+       BT_DBG("%p", conn);
+
+       conn->state = BT_CONNECT;
+       conn->out = 1;
+
+       memset(&cp, 0, sizeof(cp));
+
+       cp.handle = phy_handle;
+       hci_build_ext_flow_spec(&cp.tx_flow_spec, tx);
+       hci_build_ext_flow_spec(&cp.rx_flow_spec, rx);
+       if (amp_role == HCI_AMP_ROLE_INITIATOR)
+               hci_send_cmd(hdev, HCI_OP_CREATE_LOGICAL_LINK, sizeof(cp), &cp);
+       else
+               hci_send_cmd(hdev, HCI_OP_ACCEPT_LOGICAL_LINK, sizeof(cp), &cp);
+}
+
+struct hci_conn *hci_loglink_create(void *handle, struct ext_flow_spec *tx, struct ext_flow_spec *rx)
+{
+       struct hci_phy_link *plink = (void *) handle;
+       struct hci_dev *hdev = plink->hdev;
+       struct hci_conn *conn;
+
+       BT_DBG("%s phylink:%p", hdev->name, plink);
+
+       /* acl link over BREDR is in BT_CONNECTED state. */
+       plink->acl_link_state = BT_CONNECTED;
+
+       conn = hci_loglink_add(plink, tx->id, ACL_LINK, HCI_AMP_ROLE_INITIATOR);
+       if (!conn)
+               return NULL;
+
+       hci_loglink_connect(conn, plink->handle, HCI_AMP_ROLE_INITIATOR, tx, rx);
+       return conn;
+}
+EXPORT_SYMBOL(hci_loglink_create);
+
+struct hci_conn *hci_loglink_accept(void *handle, struct ext_flow_spec *tx, struct ext_flow_spec *rx)
+{
+       struct hci_phy_link *plink = (void *) handle;
+       struct hci_dev *hdev = plink->hdev;
+       struct hci_conn *conn;
+
+       BT_DBG("%s phylink:%p", hdev->name, plink);
+
+       /* acl link over BREDR is in BT_CONNECTED state. */
+       plink->acl_link_state = BT_CONNECTED;
+
+       conn = hci_loglink_add(plink, tx->id, ACL_LINK, HCI_AMP_ROLE_RESPONDER);
+       if (!conn)
+               return NULL;
+
+       hci_loglink_connect(conn, plink->handle, HCI_AMP_ROLE_RESPONDER, tx, rx);
+       return conn;
+}
+EXPORT_SYMBOL(hci_loglink_accept);
+
+void hci_loglink_modify(struct hci_conn *hconn, struct ext_flow_spec *tx, struct ext_flow_spec *rx)
+{
+       struct hci_dev *hdev = hconn->hdev;
+       struct hci_cp_flow_spec_modify cp;
+
+       if (hconn->state != BT_CONNECTED)
+               return;
+
+       cp.handle = cpu_to_le16(hconn->handle);
+       hci_build_ext_flow_spec(&cp.tx_flow_spec, tx);
+       hci_build_ext_flow_spec(&cp.rx_flow_spec, rx);
+
+       hci_send_cmd(hdev, HCI_OP_FLOW_SPEC_MODIFY, sizeof(cp), &cp);
+}
+EXPORT_SYMBOL(hci_loglink_modify);
+
+void hci_loglink_put(struct hci_conn *conn)
+{
+       struct hci_dev *hdev = conn->hdev;
+       struct hci_conn *acl_link = conn->phylink->priv_data;
+
+       BT_DBG("%s conn:%p", hdev->name, conn);
+
+       if (!hci_loglink_lookup_tx_flow_spec_id(conn->phylink,  conn->tx_flow_spec_id))
+               return;
+
+       /* save the state of the acl connection over BREDR.*/
+       conn->phylink->acl_link_state = acl_link->state;
+
+       if (conn->state == BT_CONNECTED) {
+               struct hci_cp_disconn_logical_link cp;
+
+               cp.handle = cpu_to_le16(conn->handle);
+               hci_send_cmd(hdev, HCI_OP_DISCONN_LOGICAL_LINK, sizeof(cp), &cp);
+
+       } else if (conn->state == BT_CONNECT) {
+               struct hci_cp_logical_link_cancel cp;
+
+               cp.handle = conn->phylink->handle;
+               cp.tx_flow_spec_id = conn->tx_flow_spec_id;
+
+               hci_send_cmd(hdev, HCI_OP_LOGICAL_LINK_CANCEL, sizeof(cp), &cp);
+       } else {
+               hci_proto_loglink_put_cfm(conn, 0x13);
+               hci_loglink_del(conn);
+       }
+
+}
+EXPORT_SYMBOL(hci_loglink_put);
+
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index aeb2982..712af4b 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -54,16 +54,23 @@ static void hci_cmd_task(unsigned long arg);
 static void hci_rx_task(unsigned long arg);
 static void hci_tx_task(unsigned long arg);
 static void hci_notify(struct hci_dev *hdev, int event);
+static inline void hci_amp_acldata_packet(struct hci_dev *hdev, struct sk_buff *skb);

 static DEFINE_RWLOCK(hci_task_lock);

 /* HCI device list */
 LIST_HEAD(hci_dev_list);
+EXPORT_SYMBOL(hci_dev_list);
+
 DEFINE_RWLOCK(hci_dev_list_lock);
+EXPORT_SYMBOL(hci_dev_list_lock);

 /* HCI callback list */
 LIST_HEAD(hci_cb_list);
+EXPORT_SYMBOL(hci_cb_list);
+
 DEFINE_RWLOCK(hci_cb_list_lock);
+EXPORT_SYMBOL(hci_cb_list_lock);

 /* HCI protocols */
 #define HCI_MAX_PROTO  2
@@ -78,11 +85,13 @@ int hci_register_notifier(struct notifier_block *nb)
 {
        return atomic_notifier_chain_register(&hci_notifier, nb);
 }
+EXPORT_SYMBOL(hci_register_notifier);

 int hci_unregister_notifier(struct notifier_block *nb)
 {
        return atomic_notifier_chain_unregister(&hci_notifier, nb);
 }
+EXPORT_SYMBOL(hci_unregister_notifier);

 static void hci_notify(struct hci_dev *hdev, int event)
 {
@@ -215,6 +224,12 @@ static void hci_init_req(struct hci_dev *hdev, unsigned long opt)
        /* Read Buffer Size (ACL mtu, max pkt, etc.) */
        hci_send_cmd(hdev, HCI_OP_READ_BUFFER_SIZE, 0, NULL);

+       /* 3.0+ HS Read Data Block Size */
+       hci_send_cmd(hdev, HCI_OP_READ_DATA_BLOCK_SIZE, 0, NULL);
+
+       /* 3.0+ HS Read Flow Control Mode */
+       hci_send_cmd(hdev, HCI_OP_READ_FLOW_CONTROL_MODE, 0, NULL);
+
 #if 0
        /* Host buffer size */
        {
@@ -227,17 +242,20 @@ static void hci_init_req(struct hci_dev *hdev, unsigned long opt)
        }
 #endif

-       /* Read BD Address */
-       hci_send_cmd(hdev, HCI_OP_READ_BD_ADDR, 0, NULL);
+       /*BREDR */
+       if (hdev->dev_type == HCI_BREDR) {
+               /* Read BD Address */
+               hci_send_cmd(hdev, HCI_OP_READ_BD_ADDR, 0, NULL);

-       /* Read Class of Device */
-       hci_send_cmd(hdev, HCI_OP_READ_CLASS_OF_DEV, 0, NULL);
+               /* Read Class of Device */
+               hci_send_cmd(hdev, HCI_OP_READ_CLASS_OF_DEV, 0, NULL);

-       /* Read Local Name */
-       hci_send_cmd(hdev, HCI_OP_READ_LOCAL_NAME, 0, NULL);
+               /* Read Local Name */
+               hci_send_cmd(hdev, HCI_OP_READ_LOCAL_NAME, 0, NULL);

-       /* Read Voice Setting */
-       hci_send_cmd(hdev, HCI_OP_READ_VOICE_SETTING, 0, NULL);
+               /* Read Voice Setting */
+               hci_send_cmd(hdev, HCI_OP_READ_VOICE_SETTING, 0, NULL);
+       }

        /* Optional initialization */

@@ -493,8 +511,10 @@ int hci_dev_open(__u16 dev)
                set_bit(HCI_RAW, &hdev->flags);

        /* Treat all non BR/EDR controllers as raw devices for now */
+       /*
        if (hdev->dev_type != HCI_BREDR)
                set_bit(HCI_RAW, &hdev->flags);
+       */

        if (hdev->open(hdev)) {
                ret = -EIO;
@@ -505,7 +525,6 @@ int hci_dev_open(__u16 dev)
                atomic_set(&hdev->cmd_cnt, 1);
                set_bit(HCI_INIT, &hdev->flags);

-               //__hci_request(hdev, hci_reset_req, 0, HZ);
                ret = __hci_request(hdev, hci_init_req, 0,
                                        msecs_to_jiffies(HCI_INIT_TIMEOUT));

@@ -875,8 +894,8 @@ int hci_register_dev(struct hci_dev *hdev)
        struct list_head *head = &hci_dev_list, *p;
        int i, id = 0;

-       BT_DBG("%p name %s bus %d owner %p", hdev, hdev->name,
-                                               hdev->bus, hdev->owner);
+       BT_DBG("%p name %s bus %d owner %p dev_type %d", hdev, hdev->name,
+                                               hdev->bus, hdev->owner, hdev->dev_type);

        if (!hdev->open || !hdev->close || !hdev->destruct)
                return -EINVAL;
@@ -906,7 +925,7 @@ int hci_register_dev(struct hci_dev *hdev)
        hdev->sniff_max_interval = 800;
        hdev->sniff_min_interval = 80;

-       tasklet_init(&hdev->cmd_task, hci_cmd_task,(unsigned long) hdev);
+       tasklet_init(&hdev->cmd_task, hci_cmd_task, (unsigned long) hdev);
        tasklet_init(&hdev->rx_task, hci_rx_task, (unsigned long) hdev);
        tasklet_init(&hdev->tx_task, hci_tx_task, (unsigned long) hdev);

@@ -925,6 +944,7 @@ int hci_register_dev(struct hci_dev *hdev)
        hci_conn_hash_init(hdev);

        INIT_LIST_HEAD(&hdev->blacklist.list);
+       INIT_LIST_HEAD(&hdev->phy_links);

        memset(&hdev->stat, 0, sizeof(struct hci_dev_stats));

@@ -1277,7 +1297,7 @@ static void hci_add_acl_hdr(struct sk_buff *skb, __u16 handle, __u16 flags)
        hdr->dlen   = cpu_to_le16(len);
 }

-void hci_send_acl(struct hci_conn *conn, struct sk_buff *skb, __u16 flags)
+int hci_send_acl(struct hci_conn *conn, struct sk_buff *skb, __u16 flags)
 {
        struct hci_dev *hdev = conn->hdev;
        struct sk_buff *list;
@@ -1286,7 +1306,12 @@ void hci_send_acl(struct hci_conn *conn, struct sk_buff *skb, __u16 flags)

        skb->dev = (void *) hdev;
        bt_cb(skb)->pkt_type = HCI_ACLDATA_PKT;
-       hci_add_acl_hdr(skb, conn->handle, flags | ACL_START);
+
+       if (hdev->dev_type == HCI_80211) {
+               BT_DBG("conn->handle:0x%x  pkt_len:%d", conn->handle, skb->len);
+               hci_add_acl_hdr(skb, conn->handle, flags | ACL_COMPLETE);
+       } else
+               hci_add_acl_hdr(skb, conn->handle, flags | ACL_START);

        if (!(list = skb_shinfo(skb)->frag_list)) {
                /* Non fragmented */
@@ -1308,7 +1333,10 @@ void hci_send_acl(struct hci_conn *conn, struct sk_buff *skb, __u16 flags)

                        skb->dev = (void *) hdev;
                        bt_cb(skb)->pkt_type = HCI_ACLDATA_PKT;
-                       hci_add_acl_hdr(skb, conn->handle, flags | ACL_CONT);
+                       if (hdev->dev_type == HCI_80211)
+                               hci_add_acl_hdr(skb, conn->handle, flags | ACL_COMPLETE);
+                       else
+                               hci_add_acl_hdr(skb, conn->handle, flags | ACL_CONT);

                        BT_DBG("%s frag %p len %d", hdev->name, skb, skb->len);

@@ -1319,17 +1347,24 @@ void hci_send_acl(struct hci_conn *conn, struct sk_buff *skb, __u16 flags)
        }

        tasklet_schedule(&hdev->tx_task);
+
+       return 0;
 }
 EXPORT_SYMBOL(hci_send_acl);

 /* Send SCO data */
-void hci_send_sco(struct hci_conn *conn, struct sk_buff *skb)
+int hci_send_sco(struct hci_conn *conn, struct sk_buff *skb)
 {
        struct hci_dev *hdev = conn->hdev;
        struct hci_sco_hdr hdr;

        BT_DBG("%s len %d", hdev->name, skb->len);

+       if (skb->len > hdev->sco_mtu) {
+               kfree_skb(skb);
+               return -EINVAL;
+       }
+
        hdr.handle = cpu_to_le16(conn->handle);
        hdr.dlen   = skb->len;

@@ -1342,6 +1377,8 @@ void hci_send_sco(struct hci_conn *conn, struct sk_buff *skb)

        skb_queue_tail(&conn->data_q, skb);
        tasklet_schedule(&hdev->tx_task);
+
+       return 0;
 }
 EXPORT_SYMBOL(hci_send_sco);

@@ -1435,6 +1472,77 @@ static inline void hci_sched_acl(struct hci_dev *hdev)
        }
 }

+static inline struct hci_conn *hci_amp_low_sent(struct hci_dev *hdev, __u8 type, int *quote)
+{
+
+       struct hci_conn *conn = NULL;
+       struct list_head *p;
+       struct list_head *p2;
+       struct hci_phy_link *plink;
+       int num = 0, min = ~0;
+
+       list_for_each(p, &hdev->phy_links) {
+               plink = list_entry(p, struct hci_phy_link, list);
+               list_for_each(p2, &plink->log_links) {
+                       struct hci_conn *c;
+                       c = list_entry(p2, struct hci_conn, list);
+
+                       if (c->type != type || skb_queue_empty(&c->data_q))
+                               continue;
+
+                       if (c->state != BT_CONNECTED && c->state != BT_CONFIG)
+                               continue;
+
+                       num++;
+
+                       if (c->sent < min) {
+                               min = c->sent;
+                               conn = c;
+                       }
+               }
+       }
+
+       if (conn) {
+               int cnt = (type == ACL_LINK ? hdev->acl_cnt : hdev->sco_cnt);
+               int q = cnt / num;
+               *quote = q ? q : 1;
+       } else
+               *quote = 0;
+
+       BT_DBG("conn %p quote %d", conn, *quote);
+       return conn;
+}
+
+
+static inline void hci_amp_sched_acl(struct hci_dev *hdev)
+{
+       struct hci_conn *conn;
+       struct sk_buff *skb;
+       int quote;
+
+       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->acl_cnt && time_after(jiffies, hdev->acl_last_tx + HZ * 45)) {
+                       BT_ERR("%s acl tx timeout", hdev->name);
+                       /*TODO: disconnect logical link connection here. */
+                       /* hci_acl_tx_to(hdev); */
+               }
+       }
+
+       while (hdev->acl_cnt && (conn = hci_amp_low_sent(hdev, ACL_LINK, &quote))) {
+               while (quote-- && (skb = skb_dequeue(&conn->data_q))) {
+                       hci_send_frame(skb);
+                       hdev->acl_last_tx = jiffies;
+
+                       hdev->acl_cnt--;
+                       conn->sent++;
+               }
+       }
+}
+
 /* Schedule SCO */
 static inline void hci_sched_sco(struct hci_dev *hdev)
 {
@@ -1487,12 +1595,15 @@ static void hci_tx_task(unsigned long arg)

        /* Schedule queues and send stuff to HCI driver */

-       hci_sched_acl(hdev);
-
-       hci_sched_sco(hdev);
+       if (hdev->dev_type == HCI_80211) {
+               hci_amp_sched_acl(hdev);
+       } else {
+               hci_sched_acl(hdev);

-       hci_sched_esco(hdev);
+               hci_sched_sco(hdev);

+               hci_sched_esco(hdev);
+       }
        /* Send next queued raw (unknown type) packet */
        while ((skb = skb_dequeue(&hdev->raw_q)))
                hci_send_frame(skb);
@@ -1614,12 +1725,19 @@ static void hci_rx_task(unsigned long arg)

                case HCI_ACLDATA_PKT:
                        BT_DBG("%s ACL data packet", hdev->name);
-                       hci_acldata_packet(hdev, skb);
+                       if (hdev->dev_type == HCI_80211)
+                               hci_amp_acldata_packet(hdev, skb);
+                       else
+                               hci_acldata_packet(hdev, skb);
+
                        break;

                case HCI_SCODATA_PKT:
                        BT_DBG("%s SCO data packet", hdev->name);
-                       hci_scodata_packet(hdev, skb);
+                       if (hdev->dev_type == HCI_80211)
+                               kfree_skb(skb);
+                       else
+                               hci_scodata_packet(hdev, skb);
                        break;

                default:
@@ -1639,6 +1757,10 @@ static void hci_cmd_task(unsigned long arg)
        BT_DBG("%s cmd %d", hdev->name, atomic_read(&hdev->cmd_cnt));

        if (!atomic_read(&hdev->cmd_cnt) && time_after(jiffies, hdev->cmd_last_tx + HZ)) {
+               if (hdev->sent_cmd) {
+                       struct hci_command_hdr *hdr = (void *) hdev->sent_cmd;
+                       BT_ERR("cmd opcode: 0x%04x", hdr->opcode);
+               }
                BT_ERR("%s command tx timeout", hdev->name);
                atomic_set(&hdev->cmd_cnt, 1);
        }
@@ -1657,3 +1779,186 @@ static void hci_cmd_task(unsigned long arg)
                }
        }
 }
+
+static struct hci_phy_link *hci_phylink_add(struct hci_dev *hdev, __u8 phy_handle, __u8 amp_role,
+                                                        __u8 *rem_assoc, __u16 assoc_size)
+{
+       struct hci_phy_link *plink;
+
+       BT_DBG("%s handle:%d", hdev->name, phy_handle);
+
+       plink = kzalloc(sizeof(struct hci_phy_link), GFP_ATOMIC);
+       if (!plink)
+               return NULL;
+
+       plink->handle = phy_handle;
+       plink->hdev = hdev;
+       plink->remote_assoc.offset = 0;
+       plink->amp_role = amp_role;
+       if (assoc_size > HCI_MAX_AMP_ASSOC_SIZE)
+               plink->remote_assoc.len = HCI_MAX_AMP_ASSOC_SIZE;
+       else
+               plink->remote_assoc.len = assoc_size;
+
+       BT_DBG("plink->remote_assoc.len:%d", plink->remote_assoc.len);
+       memcpy(plink->remote_assoc.data, rem_assoc, plink->remote_assoc.len);
+       INIT_LIST_HEAD(&plink->log_links);
+
+       plink->state = BT_OPEN;
+
+       list_add(&plink->list, &hdev->phy_links);
+
+       return plink;
+
+}
+
+int hci_phylink_del(struct hci_phy_link *plink)
+{
+       BT_DBG("del phylink:%p", plink);
+       list_del(&plink->list);
+       kfree(plink);
+       return 0;
+}
+
+static inline void hci_amp_acldata_packet(struct hci_dev *hdev, struct sk_buff *skb)
+{
+       struct hci_acl_hdr *hdr = (void *) skb->data;
+       struct hci_phy_link *plink;
+       __u16 handle, flags;
+
+       skb_pull(skb, HCI_ACL_HDR_SIZE);
+
+       handle = __le16_to_cpu(hdr->handle);
+       flags  = hci_flags(handle);
+       handle = hci_handle(handle);
+
+       BT_DBG("%s len %d handle 0x%x flags 0x%x", hdev->name, skb->len, handle, flags);
+
+       hdev->stat.acl_rx++;
+
+       hci_dev_lock(hdev);
+       plink = hci_phylink_lookup_handle(hdev, handle);
+       hci_dev_unlock(hdev);
+
+       if (plink && plink->priv_data) {
+               register struct hci_proto *hp;
+
+               /* Send to upper protocol */
+               hp = hci_proto[HCI_PROTO_L2CAP];
+               if (hp && hp->recv_acldata
+                       && plink->acl_link_state == BT_CONNECTED) {
+                       hp->recv_acldata(plink->priv_data, skb, flags);
+                       return;
+               }
+       } else {
+               BT_ERR("%s ACL packet for unknown connection handle %d",
+                       hdev->name, handle);
+       }
+
+       kfree_skb(skb);
+}
+
+/* ------ AMP HCI interface for upper layer ----- */
+struct hci_phy_link *hci_phylink_create(struct hci_dev *hdev, __u8 phy_handle,
+                               struct amp_link_key *key, __u8 *rem_assoc, __u16 assoc_size, void* priv_data)
+{
+       struct hci_phy_link *plink;
+
+       BT_DBG("%s handle:%d, key_len:%d assoc_size:%d", hdev->name, phy_handle, key->key_len, assoc_size);
+
+       if (!test_bit(HCI_UP, &hdev->flags))
+               return NULL;
+
+       hci_dev_lock(hdev);
+       plink = hci_phylink_lookup_handle(hdev, phy_handle);
+       if (!plink) {
+               plink = hci_phylink_add(hdev, phy_handle, HCI_AMP_ROLE_INITIATOR, rem_assoc, assoc_size);
+               if (!plink) {
+                       hci_dev_unlock(hdev);
+                       return NULL;
+               }
+       }
+       plink->priv_data = priv_data;
+       hci_dev_unlock(hdev);
+
+       if (plink->state == BT_OPEN || plink->state == BT_CLOSED) {
+               struct hci_cp_create_physical_link cp;
+
+               plink->state = BT_CONNECT;
+               cp.handle = phy_handle;
+               cp.key_type = key->key_type;
+
+               cp.key_len = key->key_len > HCI_MAX_AMP_KEY_SIZE ? \
+                            HCI_MAX_AMP_KEY_SIZE : key->key_len;
+
+               BT_DBG("cp.key_len %d, cp.handle %d", cp.key_len, cp.handle);
+               memcpy(cp.key, key->key, cp.key_len);
+               hci_send_cmd(hdev, HCI_OP_CREATE_PHYSICAL_LINK, sizeof(cp), &cp);
+       }
+       BT_DBG("end");
+       return plink;
+
+}
+EXPORT_SYMBOL(hci_phylink_create);
+
+struct hci_phy_link *hci_phylink_accept(struct hci_dev *hdev, __u8 phy_handle,
+                                       struct amp_link_key *key, __u8 *rem_assoc, __u16 assoc_size, void* priv_data)
+{
+       struct hci_phy_link *plink;
+
+       BT_DBG("%s handle:%d, key_len:%d assoc_size:%d", hdev->name, phy_handle, key->key_len, assoc_size);
+
+       hci_dev_lock(hdev);
+       plink = hci_phylink_lookup_handle(hdev, phy_handle);
+       if (!plink) {
+               plink = hci_phylink_add(hdev, phy_handle, HCI_AMP_ROLE_RESPONDER, rem_assoc, assoc_size);
+               if (!plink) {
+                       hci_dev_unlock(hdev);
+                       return NULL;
+               }
+       }
+       plink->priv_data = priv_data;
+       hci_dev_unlock(hdev);
+
+       if (plink->state == BT_OPEN || plink->state == BT_CLOSED) {
+               struct hci_cp_accept_physical_link cp;
+
+               plink->state = BT_CONNECT;
+               cp.handle = phy_handle;
+               cp.key_type = key->key_type;
+               cp.key_len = key->key_len > HCI_MAX_AMP_KEY_SIZE ? HCI_MAX_AMP_KEY_SIZE : key->key_len;
+               BT_DBG("cp.key_len %d, cp.handle %d", cp.key_len, cp.handle);
+               memcpy(cp.key, key->key, cp.key_len);
+               hci_send_cmd(hdev, HCI_OP_ACCEPT_PHYSICAL_LINK, sizeof(cp), &cp);
+       }
+       BT_DBG("end");
+       return plink;
+}
+
+EXPORT_SYMBOL(hci_phylink_accept);
+
+void hci_phylink_put(struct hci_phy_link *phy_link, __u8 reason)
+{
+       struct hci_cp_disconn_physical_link cp;
+
+       BT_DBG("put physical link:%p", phy_link);
+
+       phy_link->state = BT_DISCONN;
+
+       cp.handle = phy_link->handle;
+       cp.reason = reason;
+       hci_send_cmd(phy_link->hdev, HCI_OP_DISCONN_PHYSICAL_LINK, sizeof(cp), &cp);
+
+}
+EXPORT_SYMBOL(hci_phylink_put);
+
+void hci_read_local_amp_info(struct hci_dev *hdev)
+{
+       BT_DBG("%s", hdev->name);
+
+       hci_send_cmd(hdev, HCI_OP_READ_LOCAL_AMP_INFO, 0, NULL);
+}
+EXPORT_SYMBOL(hci_read_local_amp_info);
+
+
+
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index a969800..080f979 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -182,7 +182,17 @@ static void hci_cc_reset(struct hci_dev *hdev, struct sk_buff *skb)
        __u8 status = *((__u8 *) skb->data);

        BT_DBG("%s status 0x%x", hdev->name, status);
-
+/*
+       if (hdev->dev_type = HCI_80211) {
+               if (test_and_clear_bit(HCI_INIT, &hdev->flags)) {
+                       if (!status) {
+                               hci_dev_hold(hdev);
+                               set_bit(HCI_UP, &hdev->flags);
+                               hci_notify(hdev, HCI_DEV_UP);
+                       }
+               }
+       }
+*/
        hci_req_complete(hdev, status);
 }

@@ -814,6 +824,550 @@ static void hci_cs_exit_sniff_mode(struct hci_dev *hdev, __u8 status)
        hci_dev_unlock(hdev);
 }

+static void hci_cc_link_key_reply(struct hci_dev *hdev, struct sk_buff *skb)
+{
+       struct hci_rp_link_key_reply *rp = (void *) skb->data;
+
+       BT_DBG("%s status 0x%x", hdev->name, rp->status);
+
+       if (!rp->status) {
+               struct hci_conn *conn;
+               struct hci_cp_link_key_reply *cp = hci_sent_cmd_data(hdev, HCI_OP_LINK_KEY_REPLY);
+
+               if (!cp)
+                       return;
+
+               hci_dev_lock(hdev);
+
+               conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr);
+               if (conn) {
+                       hci_conn_hold(conn);
+                       memcpy(conn->link_key, cp->link_key, 16);
+                       conn->key_type =  0x04;
+
+                       hci_conn_put(conn);
+               }
+               hci_dev_unlock(hdev);
+       }
+}
+
+static void hci_write_rmt_assoc(struct hci_dev *hdev, struct hci_phy_link *plink)
+{
+       __u16 len;
+       __u16 remain;
+       struct hci_cp_write_remote_amp_assoc cp;
+       struct amp_assoc *rmt_assoc = &plink->remote_assoc;
+
+       remain = rmt_assoc->len - rmt_assoc->offset;
+       len = min_t(__u16, remain, HCI_MAX_AMP_ASSOC_FRAGMENT);
+
+       cp.handle = plink->handle;
+       cp.amp_assoc_remaining_len = cpu_to_le16(remain);
+       cp.len_so_far = cpu_to_le16(rmt_assoc->offset);
+       memcpy(cp.amp_assoc_fragment, rmt_assoc->data + rmt_assoc->offset, len);
+       rmt_assoc->offset += len;
+       hci_send_cmd(hdev, HCI_OP_WRITE_REMOTE_AMP_ASSOC, sizeof(cp) - HCI_MAX_AMP_ASSOC_FRAGMENT + len, &cp);
+}
+
+static void hci_cc_read_data_block_size(struct hci_dev *hdev, struct sk_buff *skb)
+{
+       struct hci_rp_read_data_block_size *rp = (void *)skb->data;
+       u16 num_blks;
+       u16 blk_size;
+       u16 max_pkt_len;
+
+       BT_DBG("%s status:0x%x", hdev->name, rp->status);
+       if (rp->status)
+               return;
+
+       hci_dev_lock(hdev);
+       num_blks = __le16_to_cpu(rp->num_blks);
+       blk_size = __le16_to_cpu(rp->max_blk_len);
+       max_pkt_len = __le16_to_cpu(rp->max_pkt_len);
+
+       hdev->acl_mtu = max_pkt_len;
+       hdev->acl_pkts = (num_blks * blk_size) / max_pkt_len;
+       hdev->acl_cnt = hdev->acl_pkts;
+       BT_ERR("%s acl mtu %d:%d", hdev->name, hdev->acl_mtu, hdev->acl_pkts);
+       BT_ERR("num_blks %d blk_size %d max_pkt_len %d", num_blks, blk_size, max_pkt_len);
+
+       hci_dev_unlock(hdev);
+
+}
+
+static void hci_cc_read_flow_control_mode(struct hci_dev *hdev, struct sk_buff *skb)
+{
+       struct hci_rp_read_flow_control_mode *rp = (void *)skb->data;
+
+       BT_DBG("%s status:0x%x", hdev->name, rp->status);
+}
+
+static void hci_cc_write_remote_amp_assoc(struct hci_dev *hdev, struct sk_buff *skb)
+{
+       struct hci_rp_write_remote_amp_assoc *rp = (void *)skb->data;
+       struct hci_phy_link *plink;
+
+       BT_DBG("%s status 0x%x", hdev->name, rp->status);
+
+       if (rp->status)
+               return;
+
+       hci_dev_lock(hdev);
+
+       plink = hci_phylink_lookup_handle(hdev, rp->handle);
+       if (plink) {
+               if (plink->remote_assoc.offset < plink->remote_assoc.len)
+                       hci_write_rmt_assoc(hdev, plink);
+               else if (plink->amp_role == HCI_AMP_ROLE_RESPONDER)
+                       hci_create_physical_link_cfm(hdev, rp->handle, HCI_CREATE_PHYLINK_PEND);
+       }
+       hci_dev_unlock(hdev);
+
+}
+
+static void hci_read_loc_assoc(struct hci_dev *hdev, __u8 phy_handle)
+{
+       struct hci_cp_read_local_amp_assoc cp;
+       struct amp_assoc *loc_assoc = &hdev->local_assoc;
+
+       cp.handle = phy_handle;
+       /*TODO: shall set this parameter to the "AMP_ASSOC_Size"
+       value returned from the remote device in the AMP Get Info Response, use A2MP MTU here right now. */
+       cp.max_remote_amp_assoc_len = cpu_to_le16(670);
+       cp.len_so_far = cpu_to_le16(loc_assoc->offset);
+       hci_send_cmd(hdev, HCI_OP_READ_LOCAL_AMP_ASSOC, sizeof(cp), &cp);
+}
+
+static void hci_cc_read_local_amp_assoc(struct hci_dev *hdev, struct sk_buff *skb)
+{
+       struct hci_rp_read_local_amp_assoc *rp = (void *)skb->data;
+       struct amp_assoc *loc_assoc = &hdev->local_assoc;
+       __u16 remain;
+       BT_DBG("%s status 0x%x", hdev->name, rp->status);
+
+       hci_dev_lock(hdev);
+
+       if (!rp->status) {
+               remain = __le16_to_cpu(rp->amp_assoc_remaining_len);
+               if (remain > HCI_MAX_AMP_ASSOC_FRAGMENT) {
+                       memcpy(loc_assoc->data + loc_assoc->offset, rp->amp_assoc_fragment, HCI_MAX_AMP_ASSOC_FRAGMENT);
+                       loc_assoc->offset += HCI_MAX_AMP_ASSOC_FRAGMENT;
+                       hci_read_loc_assoc(hdev, rp->handle);
+               } else {
+                       memcpy(loc_assoc->data + loc_assoc->offset, rp->amp_assoc_fragment, remain);
+                       loc_assoc->len = loc_assoc->offset + remain;
+                       loc_assoc->offset = 0;
+                       if (rp->handle == 0)
+                               hci_local_assoc_ind(hdev, rp->status);
+                       else
+                               hci_create_physical_link_cfm(hdev, rp->handle, HCI_CREATE_PHYLINK_PEND);
+               }
+       }
+
+       hci_dev_unlock(hdev);
+}
+
+static void hci_cc_read_local_amp_info(struct hci_dev *hdev, struct sk_buff *skb)
+{
+       struct hci_rp_read_local_amp_info *rp = (void *)skb->data;
+
+       BT_DBG("%s status:%d", hdev->name, rp->status);
+
+       hci_dev_lock(hdev);
+
+       if (!rp->status) {
+               hdev->ctrl_info.amp_status = rp->amp_status;
+               hdev->ctrl_info.total_bandwidth = __le32_to_cpu(rp->total_bandwidth);
+               hdev->ctrl_info.max_guaranteed_bandwidth = __le32_to_cpu(rp->max_guaranteed_bandwidth);
+               hdev->ctrl_info.min_latency = __le32_to_cpu(rp->min_latency);
+               hdev->ctrl_info.max_pdu_size = __le32_to_cpu(rp->max_pdu_size);
+               hdev->ctrl_info.ctrl_type = rp->controller_type;
+               hdev->ctrl_info.pal_caps = __le16_to_cpu(rp->pal_caps);
+               hdev->ctrl_info.max_assoc_len = __le16_to_cpu(rp->max_amp_assoc_len);
+               hdev->ctrl_info.max_flush_to = __le32_to_cpu(rp->max_flush_timeout);
+               hdev->ctrl_info.best_effort_flush_to = __le32_to_cpu(rp->best_effort_flush_timeout);
+       }
+       hci_local_info_ind(hdev, rp->status);
+
+       hci_dev_unlock(hdev);
+
+}
+
+void hci_read_local_amp_assoc(struct hci_dev *hdev)
+{
+       if (!test_bit(HCI_UP, &hdev->flags))
+               return;
+
+       hdev->local_assoc.len = 0;
+       hdev->local_assoc.offset = 0;
+       hci_read_loc_assoc(hdev, 0x00);
+}
+EXPORT_SYMBOL(hci_read_local_amp_assoc);
+
+static void hci_phylink_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
+{
+       struct hci_ev_physical_link_complete *ev = (void *)skb->data;
+
+       BT_DBG("%s status:%d handle:%d", hdev->name, ev->status, ev->handle);
+
+       hci_dev_lock(hdev);
+
+
+       if (!ev->status) {
+               struct hci_phy_link *plink = hci_phylink_lookup_handle(hdev, ev->handle);
+               if (plink)
+                       plink->state = BT_CONNECTED;
+       }
+
+       hci_create_physical_link_cfm(hdev, ev->handle, ev->status);
+
+       hci_dev_unlock(hdev);
+}
+
+static void hci_disconn_phylink_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
+{
+       struct hci_ev_disconn_physical_link_complete *ev = (void *)skb->data;
+       struct hci_phy_link *plink;
+
+       BT_DBG("%s status %d", hdev->name, ev->status);
+
+       hci_dev_lock(hdev);
+
+       plink = hci_phylink_lookup_handle(hdev, ev->handle);
+       if (plink) {
+               plink->state = BT_CLOSED;
+               hci_put_physical_link_cfm(hdev, ev->handle, ev->status, ev->reason);
+               hci_phylink_del(plink);
+       }
+       hci_dev_unlock(hdev);
+}
+
+static void hci_cs_create_phylink(struct hci_dev *hdev, __u8 status)
+{
+       struct hci_cp_create_physical_link *cp;
+       struct hci_phy_link *plink;
+
+       BT_DBG("%s status 0x%x", hdev->name, status);
+
+       cp = hci_sent_cmd_data(hdev, HCI_OP_CREATE_PHYSICAL_LINK);
+       if (!cp)
+               return;
+
+       hci_dev_lock(hdev);
+
+       plink = hci_phylink_lookup_handle(hdev, cp->handle);
+       if (plink) {
+               if (!status) {
+                       plink->remote_assoc.offset = 0;
+                       hci_write_rmt_assoc(hdev, plink);
+               } else {
+                       hci_create_physical_link_cfm(hdev, plink->handle, status);
+                       hci_phylink_del(plink);
+               }
+       }
+
+       hci_dev_unlock(hdev);
+}
+
+static void hci_cs_accept_phylink(struct hci_dev *hdev, __u8 status)
+{
+       struct hci_cp_accept_physical_link *cp;
+       struct hci_phy_link *plink;
+
+       BT_DBG("%s status 0x%x", hdev->name, status);
+
+       cp = hci_sent_cmd_data(hdev, HCI_OP_ACCEPT_PHYSICAL_LINK);
+       if (!cp)
+               return;
+
+       hci_dev_lock(hdev);
+
+       plink = hci_phylink_lookup_handle(hdev, cp->handle);
+
+       if (status) {
+               hci_create_physical_link_cfm(hdev, cp->handle, status);
+               if (plink)
+                       hci_phylink_del(plink);
+       } else {
+               if (plink) {
+                       plink->remote_assoc.offset = 0;
+                       hci_write_rmt_assoc(hdev, plink);
+               }
+       }
+
+       hci_dev_unlock(hdev);
+}
+
+static void hci_cs_disconn_phylink(struct hci_dev *hdev, __u8 status)
+{
+       struct hci_cp_disconn_physical_link *cp;
+
+       BT_DBG("%s status %d", hdev->name, status);
+
+       cp = hci_sent_cmd_data(hdev, HCI_OP_DISCONN_PHYSICAL_LINK);
+       if (!cp)
+               return;
+
+       hci_dev_lock(hdev);
+
+       if (status) {
+               struct hci_phy_link *plink;
+
+               hci_put_physical_link_cfm(hdev, cp->handle, status, cp->reason);
+               plink = hci_phylink_lookup_handle(hdev, cp->handle);
+               if (plink)
+                       hci_phylink_del(plink);
+       }
+
+       hci_dev_unlock(hdev);
+}
+
+static void hci_loglink_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
+{
+       struct hci_ev_logical_link_complete *ev = (void *)skb->data;
+       struct hci_phy_link *plink;
+       struct hci_conn *conn;
+
+       BT_DBG("%s status:%d", hdev->name, ev->status);
+       hci_dev_lock(hdev);
+
+       plink = hci_phylink_lookup_handle(hdev, ev->physical_link_handle);
+       if (plink) {
+               conn = hci_loglink_lookup_tx_flow_spec_id(plink, ev->tx_flow_spec_id);
+               if (conn) {
+                       if (!ev->status) {
+                               conn->state = BT_CONNECTED;
+                               conn->handle = ev->logical_link_handle;
+                       }
+                       if (conn->amp_role == HCI_AMP_ROLE_INITIATOR)
+                               hci_proto_loglink_create_cfm(conn, ev->status);
+                       else
+                               hci_proto_loglink_accept_cfm(conn, ev->status);
+               }
+       }
+       hci_dev_unlock(hdev);
+}
+
+static void hci_cs_create_loglink(struct hci_dev *hdev, __u8 status)
+{
+       struct hci_cp_create_logical_link *cp;
+
+       BT_DBG("%s status %d", hdev->name, status);
+
+       cp = hci_sent_cmd_data(hdev, HCI_OP_CREATE_LOGICAL_LINK);
+       if (!cp)
+               return;
+
+       hci_dev_lock(hdev);
+
+       if (status) {
+               struct hci_phy_link *plink;
+               struct hci_conn *conn;
+               plink = hci_phylink_lookup_handle(hdev, cp->handle);
+               if (plink) {
+                       conn = hci_loglink_lookup_tx_flow_spec_id(plink, cp->tx_flow_spec.id);
+                       if (conn) {
+                               hci_proto_loglink_create_cfm(conn, status);
+                               hci_loglink_del(conn);
+                       }
+               }
+       }
+
+       hci_dev_unlock(hdev);
+
+}
+
+static void hci_cs_accept_loglink(struct hci_dev *hdev, __u8 status)
+{
+       struct hci_cp_accept_logical_link *cp;
+
+       BT_DBG("%s status %d", hdev->name, status);
+
+       cp = hci_sent_cmd_data(hdev, HCI_OP_ACCEPT_LOGICAL_LINK);
+       if (!cp)
+               return;
+
+       hci_dev_lock(hdev);
+
+       if (status) {
+               struct hci_phy_link *plink;
+               struct hci_conn *conn;
+               plink = hci_phylink_lookup_handle(hdev, cp->handle);
+               if (plink) {
+                       conn = hci_loglink_lookup_tx_flow_spec_id(plink, cp->tx_flow_spec.id);
+                       if (conn) {
+                               hci_proto_loglink_create_cfm(conn, status);
+                               hci_loglink_del(conn);
+                       }
+               }
+       }
+
+       hci_dev_unlock(hdev);
+
+}
+
+static void hci_cs_disconn_loglink(struct hci_dev *hdev, __u8 status)
+{
+       struct hci_cp_disconn_logical_link *cp;
+
+       BT_DBG("%s status %d", hdev->name, status);
+
+       cp = hci_sent_cmd_data(hdev, HCI_OP_DISCONN_LOGICAL_LINK);
+       if (!cp)
+               return;
+
+       hci_dev_lock(hdev);
+       if (status) {
+               struct hci_conn *conn;
+               __u16 handle = __le16_to_cpu(cp->handle);
+               conn = hci_loglink_lookup_handle(hdev, handle);
+               if (conn) {
+                       if (conn->phylink->acl_link_state == BT_CONNECTED)
+                               hci_proto_loglink_put_cfm(conn, status);
+
+                       hci_loglink_del(conn);
+               }
+       }
+       hci_dev_unlock(hdev);
+}
+
+static void hci_loglink_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
+{
+       struct hci_ev_disconn_logical_link_complete *ev = (void *)skb->data;
+       struct hci_conn *conn;
+       __u16 handle;
+
+       BT_DBG("%s status %d", hdev->name, ev->status);
+
+       hci_dev_lock(hdev);
+
+       handle = __le16_to_cpu(ev->handle);
+       conn = hci_loglink_lookup_handle(hdev, handle);
+       if (conn) {
+               if (!ev->status)
+                       conn->state = BT_CLOSED;
+
+               if (conn->phylink->acl_link_state == BT_CONNECTED)
+                       hci_proto_loglink_put_cfm(conn, ev->status);
+
+               hci_loglink_del(conn);
+       }
+
+       hci_dev_unlock(hdev);
+
+}
+
+static void hci_cc_cancel_loglink(struct hci_dev *hdev, struct sk_buff *skb)
+{
+       struct hci_rp_logical_link_cancel *rp = (void *)skb->data;
+       struct hci_phy_link *plink;
+       struct hci_conn *conn;
+
+       BT_DBG("%s status %d", hdev->name,  rp->status);
+
+       hci_dev_lock(hdev);
+
+       plink = hci_phylink_lookup_handle(hdev, rp->handle);
+       if (plink) {
+               conn = hci_loglink_lookup_tx_flow_spec_id(plink, rp->tx_flow_spec_id);
+               if (conn) {
+                       if (!rp->status)
+                               conn->state = BT_CLOSED;
+
+                       if (conn->phylink->acl_link_state == BT_CONNECTED)
+                               hci_proto_loglink_put_cfm(conn, rp->status);
+
+                       hci_loglink_del(conn);
+               }
+       }
+
+       hci_dev_unlock(hdev);
+}
+
+static void hci_cs_flow_spec_modify(struct hci_dev *hdev, __u8 status)
+{
+       struct hci_cp_flow_spec_modify *cp;
+       struct hci_conn *conn;
+       __u16 handle;
+
+       BT_DBG("%s status %d", hdev->name, status);
+
+       cp = hci_sent_cmd_data(hdev, HCI_OP_FLOW_SPEC_MODIFY);
+       if (!cp)
+               return;
+
+       hci_dev_lock(hdev);
+
+       handle = __le16_to_cpu(cp->handle);
+       if (status) {
+               conn = hci_loglink_lookup_handle(hdev, handle);
+               if (conn) {
+                       hci_proto_loglink_modify_cfm(conn, status);
+               }
+       }
+
+       hci_dev_unlock(hdev);
+}
+
+static void hci_flow_spec_modify_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
+{
+       struct hci_ev_flow_spec_modify_complete *ev = (void *)skb->data;
+       struct hci_conn *conn;
+
+       conn = hci_loglink_lookup_handle(hdev, ev->handle);
+       if (conn)
+               hci_proto_loglink_modify_cfm(conn, ev->status);
+}
+
+static void hci_amp_status_change_evt(struct hci_dev *hdev, struct sk_buff *skb)
+{
+       struct hci_ev_amp_status_change *ev = (void *)skb->data;
+       /* TODO: report amp status change event to amp mgr. */
+       (void) ev->status;
+       (void) ev->amp_status;
+       BT_ERR("%s status %d amp status %d", hdev->name, ev->status, ev->amp_status);
+}
+
+static void hci_num_comp_data_blocks_evt(struct hci_dev *hdev, struct sk_buff * skb)
+{
+       struct hci_ev_num_of_completed_data_blocks *ev = (void *)skb->data;
+       __le16 *ptr;
+       int i;
+
+       skb_pull(skb, sizeof(*ev));
+
+       BT_DBG("%s num_hnld %d", hdev->name, ev->num_handles);
+
+       if (skb->len < ev->num_handles * 6) {
+               BT_ERR("%s bad parameters", hdev->name);
+               return;
+       }
+
+       tasklet_disable(&hdev->tx_task);
+
+       for (i = 0, ptr = (__le16 *) skb->data; i < ev->num_handles; i++) {
+               struct hci_conn *conn;
+               __u16 handle, pkt_cnt, blk_cnt;
+
+               handle = get_unaligned_le16(ptr++);
+               pkt_cnt = get_unaligned_le16(ptr++);
+               blk_cnt = get_unaligned_le16(ptr++);
+               conn = hci_conn_hash_lookup_handle(hdev, handle);
+
+               BT_DBG("handle: 0x%x, conn:%p pkt_cnt:%d, blk_cnt:%d", handle,  conn, pkt_cnt, blk_cnt);
+               if (conn) {
+                       conn->sent -= pkt_cnt;
+                       hdev->acl_cnt += pkt_cnt;
+                       if (hdev->acl_cnt > hdev->acl_pkts)
+                               hdev->acl_cnt = hdev->acl_pkts;
+               }
+       }
+
+       tasklet_schedule(&hdev->tx_task);
+       tasklet_enable(&hdev->tx_task);
+
+}
+
 static inline void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
 {
        __u8 status = *((__u8 *) skb->data);
@@ -1190,6 +1744,32 @@ static inline void hci_remote_features_evt(struct hci_dev *hdev, struct sk_buff
        hci_dev_unlock(hdev);
 }

+
+static inline void hci_flush_occurred_evt(struct hci_dev *hdev, struct sk_buff *skb)
+{
+       struct hci_ev_flush_occurred *ev = (void *) skb->data;
+       __u16 handle = __le16_to_cpu(ev->handle);
+
+       BT_DBG("%s handle 0x%x", hdev->name, handle);
+       BT_ERR("%s handle 0x%x", hdev->name, handle);
+
+       if (hdev->dev_type == HCI_80211) {
+               struct hci_conn *conn;
+
+               conn = hci_loglink_lookup_handle(hdev, handle);
+
+               if (!conn)
+                       return;
+
+               tasklet_disable(&hdev->tx_task);
+
+               hdev->acl_cnt = 0;
+
+               tasklet_schedule(&hdev->tx_task);
+               tasklet_enable(&hdev->tx_task);
+       }
+}
+
 static inline void hci_remote_version_evt(struct hci_dev *hdev, struct sk_buff *skb)
 {
        BT_DBG("%s", hdev->name);
@@ -1218,6 +1798,10 @@ static inline void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *sk
                hci_cc_exit_periodic_inq(hdev, skb);
                break;

+       case HCI_OP_LINK_KEY_REPLY:
+               hci_cc_link_key_reply(hdev, skb);
+               break;
+
        case HCI_OP_REMOTE_NAME_REQ_CANCEL:
                hci_cc_remote_name_req_cancel(hdev, skb);
                break;
@@ -1314,6 +1898,31 @@ 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_READ_DATA_BLOCK_SIZE:
+               hci_cc_read_data_block_size(hdev, skb);
+               break;
+
+       case HCI_OP_READ_FLOW_CONTROL_MODE:
+               hci_cc_read_flow_control_mode(hdev, skb);
+               break;
+
+       case HCI_OP_WRITE_REMOTE_AMP_ASSOC:
+               hci_cc_write_remote_amp_assoc(hdev, skb);
+               break;
+
+       case HCI_OP_READ_LOCAL_AMP_INFO:
+               hci_cc_read_local_amp_info(hdev, skb);
+               break;
+
+       case HCI_OP_READ_LOCAL_AMP_ASSOC:
+               hci_cc_read_local_amp_assoc(hdev, skb);
+               break;
+
+       case HCI_OP_LOGICAL_LINK_CANCEL:
+               hci_cc_cancel_loglink(hdev, skb);
+               break;
+
+
        default:
                BT_DBG("%s opcode 0x%x", hdev->name, opcode);
                break;
@@ -1380,6 +1989,34 @@ 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_CREATE_PHYSICAL_LINK:
+               hci_cs_create_phylink(hdev, ev->status);
+               break;
+
+       case HCI_OP_ACCEPT_PHYSICAL_LINK:
+               hci_cs_accept_phylink(hdev, ev->status);
+               break;
+
+       case HCI_OP_DISCONN_PHYSICAL_LINK:
+               hci_cs_disconn_phylink(hdev, ev->status);
+               break;
+
+       case HCI_OP_CREATE_LOGICAL_LINK:
+               hci_cs_create_loglink(hdev, ev->status);
+               break;
+
+       case HCI_OP_ACCEPT_LOGICAL_LINK:
+               hci_cs_accept_loglink(hdev, ev->status);
+               break;
+
+       case HCI_OP_DISCONN_LOGICAL_LINK:
+               hci_cs_disconn_loglink(hdev, ev->status);
+               break;
+
+       case HCI_OP_FLOW_SPEC_MODIFY:
+               hci_cs_flow_spec_modify(hdev, ev->status);
+               break;
+
        default:
                BT_DBG("%s opcode 0x%x", hdev->name, opcode);
                break;
@@ -1522,7 +2159,11 @@ static inline void hci_link_key_notify_evt(struct hci_dev *hdev, struct sk_buff
        conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr);
        if (conn) {
                hci_conn_hold(conn);
+
                conn->disc_timeout = HCI_DISCONN_TIMEOUT;
+               memcpy(conn->link_key, ev->link_key, 16);
+               conn->key_type = ev->key_type;
+
                hci_conn_put(conn);
        }

@@ -1822,6 +2463,25 @@ static inline void hci_remote_host_features_evt(struct hci_dev *hdev, struct sk_
        hci_dev_unlock(hdev);
 }

+static inline void hci_channel_selected_evt(struct hci_dev *hdev, struct sk_buff *skb)
+{
+       struct hci_ev_channel_selected *ev = (void *)skb->data;
+       struct hci_phy_link *plink;
+
+       BT_DBG("%s", hdev->name);
+
+       hci_dev_lock(hdev);
+       plink = hci_phylink_lookup_handle(hdev, ev->handle);
+       if (plink) {
+               hdev->local_assoc.len = 0;
+               hdev->local_assoc.offset = 0;
+
+               hci_read_loc_assoc(hdev, plink->handle);
+               plink->state = BT_CONNECT2;
+       }
+       hci_dev_unlock(hdev);
+}
+
 void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
 {
        struct hci_event_hdr *hdr = (void *) skb->data;
@@ -1958,6 +2618,42 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
                hci_remote_host_features_evt(hdev, skb);
                break;

+       case HCI_EV_PHYSICAL_LINK_COMPLETE:
+               hci_phylink_complete_evt(hdev, skb);
+               break;
+
+       case HCI_EV_DISCONN_PHYSICAL_LINK_COMPLETE:
+               hci_disconn_phylink_complete_evt(hdev, skb);
+               break;
+
+       case HCI_EV_LOGICAL_LINK_COMPLETE:
+               hci_loglink_complete_evt(hdev, skb);
+               break;
+
+       case HCI_EV_DISCONN_LOGICAL_LINK_COMPLETE:
+               hci_loglink_disconn_complete_evt(hdev, skb);
+               break;
+
+       case HCI_EV_FLOW_SPEC_MODIFY_COMPLETE:
+               hci_flow_spec_modify_complete_evt(hdev, skb);
+               break;
+
+       case HCI_EV_AMP_STATUS_CHANGE:
+               hci_amp_status_change_evt(hdev, skb);
+               break;
+
+       case HCI_EV_NUM_OF_COMPLETED_DATA_BLOCKS:
+               hci_num_comp_data_blocks_evt(hdev, skb);
+               break;
+
+       case HCI_EV_CHANNEL_SELECTED:
+               hci_channel_selected_evt(hdev, skb);
+               break;
+
+       case HCI_EV_FLUSH_OCCURRED:
+               hci_flush_occurred_evt(hdev, skb);
+               break;
+
        default:
                BT_DBG("%s event 0x%x", hdev->name, event);
                break;
--
1.6.3.3


^ permalink raw reply related

* Re: SSP Link key storing issue
From: Prabhakaran Chandrasekara M @ 2010-07-15  6:38 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <AANLkTimpe_b4IpgRnTHWctF9XcYnb8vVmUCKGdn0_dKO@mail.gmail.com>

Can some body please explain why the below check is considered while
storing link key.


On Thu, Jul 15, 2010 at 10:25 AM, Prabhakaran Chandrasekara M
<prvb86@motorola.com> wrote:
> Hello All,
>
>  I am facing some problem with SSP pairing.
> Sometimes Bluez does not store the Authenticated Combination link key
> generated during pairing process And found the below code in
> dbus-hci.c
> hci_dbus_link_key_notify
>
> /* Only store the link key if one of the following is true:
>      * 1. this is a legacy link key
>      * 2. this is a changed combination key and there was a previously
>      *    stored one
>      * 3. neither local nor remote side had no-bonding as a requirement
>      * 4. the local side had dedicated bonding as a requirement
>      * 5. the remote side is using dedicated bonding since in that case
>      *    also the local requirements are set to dedicated bonding
>      */
>
> And from the logs found that, Bluez sets the Auth requirement as "no
> bonding" and remote device sets the auth requirement as "General
> Bonding", as a result of this pairing process "Authenticated
> combination key" has been generated but not stored because of the
> above mentioned check.
 (* 3. neither local nor remote side had no-bonding as a requirement)
Forgot mention the Check.
>Hence for the reconnection or other profile
> connections, Bluez would re initiate the pairing process which User
> would not like.
>
> Usecase:
> Remote device was not paired, I initiated a2dp connection through dbus
> commands to remote device, hence the l2cap security level is low and
> auth requirement is set as "No Bonding" at kernel.
>
>
> I could not find Specification points for this behavior.
>
> Can some body explain why Bluez has this check (based on some
> specification?) while storing link key.
>
> Thanks,
> Prabhakaran.
>


Thanks,
Prabhakaran.

^ permalink raw reply

* RE: [PATCH 3/3] Bluetooth: Synchronize SCO/eSCO connection requests to ACL state
From: Marcel Holtmann @ 2010-07-15  6:16 UTC (permalink / raw)
  To: Perelet, Oleg; +Cc: Matt Wilson, Ron Shaffer, linux-bluetooth@vger.kernel.org
In-Reply-To: <BCDE476F204B134B8B235FB275BC3AD810408D2A99@NALASEXMB01.na.qualcomm.com>

Hi Oleg,

> >> is there really a problem? The LMP will send an error via HCI. 
> >
> >The attempt to start SCO after the related ACL is no longer present
> >makes no sense.
> >
> >Either after mode change event with no success or disconnection complete
> >event the result is the same; there is no valid ACL to attempt
> >SCO/eSCO.
> >
> >Error code 0x02 "unknown connection identifier" may apply for the
> >command status event for setup synchronous connection command but the
> >spec does not require it.
> 
> Security classification that this case fits is partial DOS for voice services, which is 
> considered to be severe for emergency calls even for few seconds.
> 
> Depending on chip - It will take awhile (several seconds) before response will pop  
> up  and then no matter what we'll still go over sco/esco setup at top of non existing ACL as Matt says.
> 
> Because whole thing is in kernel several seconds of audio may get lost before phone(userland) 
> will realize that SCO failed and route audio to speaker, if ever:)
> 
> I'm not big fan of having all of this complication in kernel because of couple of screwed up headsets. 
> There's no problem doing similar logic in userland with tight control over particular HS and timing. 

this might break for a broken headset, but that is than a problem of the
headset. Also until the SCO setup is completed the SCO socket should
never be connected. So userspace can properly detect if SCO setup is in
progress and route it to the speaker with a proper audio policy. So I
think the emergency call argument is void. And broken headset is broken
headset. If it wants to fail, it will fail eventually anyway. The patch
gives it a chance to et this working without breaking other headsets. So
disconnect event arriving on the ACL link and SCO setup pending is
something we need to fix. That is correct. However that is a different
issues actually. Since this can happen right now as well.

Regards

Marcel



^ permalink raw reply

* RE: [PATCH 3/3] Bluetooth: Synchronize SCO/eSCO connection requests to ACL state
From: Marcel Holtmann @ 2010-07-15  6:13 UTC (permalink / raw)
  To: Matt Wilson; +Cc: Ron Shaffer, linux-bluetooth@vger.kernel.org
In-Reply-To: <1279141191.5806.1.camel@linux-champ-06.qualcomm.com>

Hi Matt,

> > is there really a problem? The LMP will send an error via HCI. So we do
> > get the mode changed event, but just with an error. And since we are not
> > checking the error at all, we just go ahead with the SCO setup attempt
> > in that case.
> > 
> 
> The attempt to start SCO after the related ACL is no longer present
> makes no sense. 
> 
> Either after mode change event with no success or disconnection complete
> event the result is the same; there is no valid ACL to attempt
> SCO/eSCO. 
> 
> Error code 0x02 "unknown connection identifier" may apply for the
> command status event for setup synchronous connection command but the
> spec does not require it.

I see the point the the disconnect event and in that case we need to
check if we have an pending SCO setup and remove that.

However what is up with the mode change. If that fails, we are still in
sniff mode and can just proceed with the SCO setup attempt. With the
broken hardware it will fail for sure, but with good behaving LM on the
remote side it might just get the device out of sniff mode at that point
and SCO setup can succeed.

Why do you imply that a mode change event with a failure means that the
ACL is no longer present?

Regards

Marcel



^ permalink raw reply

* SSP Link key storing issue
From: Prabhakaran Chandrasekara M @ 2010-07-15  4:55 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <AANLkTinYIezHD8y4D4s9_awxjdqVV2NSp0f6Tmq1FEpa@mail.gmail.com>

Hello All,

 I am facing some problem with SSP pairing.
Sometimes Bluez does not store the Authenticated Combination link key
generated during pairing process And found the below code in
dbus-hci.c
hci_dbus_link_key_notify

/* Only store the link key if one of the following is true:
     * 1. this is a legacy link key
     * 2. this is a changed combination key and there was a previously
     *    stored one
     * 3. neither local nor remote side had no-bonding as a requirement
     * 4. the local side had dedicated bonding as a requirement
     * 5. the remote side is using dedicated bonding since in that case
     *    also the local requirements are set to dedicated bonding
     */

And from the logs found that, Bluez sets the Auth requirement as "no
bonding" and remote device sets the auth requirement as "General
Bonding", as a result of this pairing process "Authenticated
combination key" has been generated but not stored because of the
above mentioned check. Hence for the reconnection or other profile
connections, Bluez would re initiate the pairing process which User
would not like.

Usecase:
Remote device was not paired, I initiated a2dp connection through dbus
commands to remote device, hence the l2cap security level is low and
auth requirement is set as "No Bonding" at kernel.


I could not find Specification points for this behavior.

Can some body explain why Bluez has this check (based on some
specification?) while storing link key.

Thanks,
Prabhakaran.

^ permalink raw reply

* RE: [PATCH 3/3] Bluetooth: Synchronize SCO/eSCO connection requests to ACL state
From: Perelet, Oleg @ 2010-07-15  3:07 UTC (permalink / raw)
  To: Matt Wilson, Marcel Holtmann; +Cc: Ron Shaffer, linux-bluetooth@vger.kernel.org
In-Reply-To: <1279141191.5806.1.camel@linux-champ-06.qualcomm.com>

Marcel.

>> is there really a problem? The LMP will send an error via HCI. 
>
>The attempt to start SCO after the related ACL is no longer present
>makes no sense.
>
>Either after mode change event with no success or disconnection complete
>event the result is the same; there is no valid ACL to attempt
>SCO/eSCO.
>
>Error code 0x02 "unknown connection identifier" may apply for the
>command status event for setup synchronous connection command but the
>spec does not require it.

Security classification that this case fits is partial DOS for voice services, which is 
considered to be severe for emergency calls even for few seconds.

Depending on chip - It will take awhile (several seconds) before response will pop  
up  and then no matter what we'll still go over sco/esco setup at top of non existing ACL as Matt says.

Because whole thing is in kernel several seconds of audio may get lost before phone(userland) 
will realize that SCO failed and route audio to speaker, if ever:)

I'm not big fan of having all of this complication in kernel because of couple of screwed up headsets. 
There's no problem doing similar logic in userland with tight control over particular HS and timing. 

Oleg.

^ permalink raw reply

* RE: [PATCH 3/3] Bluetooth: Synchronize SCO/eSCO connection requests to ACL state
From: Matt Wilson @ 2010-07-14 20:59 UTC (permalink / raw)
  To: Marcel Holtmann; +Cc: Ron Shaffer, linux-bluetooth@vger.kernel.org
In-Reply-To: <1279135803.6282.58.camel@localhost.localdomain>

Marcel,

On Wed, 2010-07-14 at 16:30 -0300, Marcel Holtmann wrote:
> is there really a problem? The LMP will send an error via HCI. So we do
> get the mode changed event, but just with an error. And since we are not
> checking the error at all, we just go ahead with the SCO setup attempt
> in that case.
> 

The attempt to start SCO after the related ACL is no longer present
makes no sense. 

Either after mode change event with no success or disconnection complete
event the result is the same; there is no valid ACL to attempt
SCO/eSCO. 

Error code 0x02 "unknown connection identifier" may apply for the
command status event for setup synchronous connection command but the
spec does not require it.

-Matt


^ permalink raw reply

* Re: [PATCH 3/3] Bluetooth: Synchronize SCO/eSCO connection requests to ACL state
From: Matthew Wilson @ 2010-07-14 20:55 UTC (permalink / raw)
  To: Marcel Holtmann
  Cc: Perelet, Oleg, Ron Shaffer, linux-bluetooth@vger.kernel.org,
	Wilson, Matt
In-Reply-To: <1279135803.6282.58.camel@localhost.localdomain>

[-- Attachment #1: Type: text/plain, Size: 755 bytes --]

Marcel,

On Jul 14, 2010, at 2:30 PM, Marcel Holtmann <marcel@holtmann.org> wrote:
>> 
>> 
>> 
> 
> is there really a problem? The LMP will send an error via HCI. So we do
> get the mode changed event, but just with an error. And since we are not
> checking the error at all, we just go ahead with the SCO setup attempt
> in that case.

The attempt to start SCO after the related ACL is no longer present makes no sense.

Either after mode change event with no success or disconnection complete event the result is the same; there is no valid ACL to attempt SCO/eSCO.

Error code 0x02 "unknown connection identifier" may apply for the command status event for setup synchronous connection command but the spec does not require it.

-Matt

[-- Attachment #2: Type: text/html, Size: 2140 bytes --]

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox