[PATCH 2/3] Add a2mp protocol/AMP manager, by Atheros Linux BT3 team.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



>From d94973250b15e03cae3299f6828f46d29e19bf33 Mon Sep 17 00:00:00 2001
From: Haijun.Liu <Haijun.Liu@xxxxxxxxxxx>
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@xxxxxxxxxxx>
---
 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

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


[Index of Archives]     [Bluez Devel]     [Linux Wireless Networking]     [Linux Wireless Personal Area Networking]     [Linux ATH6KL]     [Linux USB Devel]     [Linux Media Drivers]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Big List of Linux Books]

  Powered by Linux