prima: TDLS external control through HAL

TDLS can be enabled/disabled from HAL. mac address will be used
to identify the peer to which the link will be setup. Added
the required functionality and vendor commands in HDD to support
this.

Change-Id: I1e89194c8ad1df4b09f03b5a648249591ae2e449
CRs-Fixed: 691023
diff --git a/CORE/HDD/inc/wlan_hdd_cfg80211.h b/CORE/HDD/inc/wlan_hdd_cfg80211.h
index 01bc84d..bf65898 100644
--- a/CORE/HDD/inc/wlan_hdd_cfg80211.h
+++ b/CORE/HDD/inc/wlan_hdd_cfg80211.h
@@ -149,6 +149,11 @@
     QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_SIGNIFICANT_CHANGE = 31,
     QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_SET_SIGNIFICANT_CHANGE = 32,
     QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_RESET_SIGNIFICANT_CHANGE = 33,
+    /*EXT TDLS*/
+    QCA_NL80211_VENDOR_SUBCMD_TDLS_ENABLE = 34,
+    QCA_NL80211_VENDOR_SUBCMD_TDLS_DISABLE = 35,
+    QCA_NL80211_VENDOR_SUBCMD_TDLS_GET_STATUS = 36,
+    QCA_NL80211_VENDOR_SUBCMD_TDLS_STATE = 37,
 };
 
 enum qca_nl80211_vendor_subcmds_index {
@@ -175,6 +180,8 @@
     QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_SIGNIFICANT_CHANGE_INDEX,
     QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_SET_SIGNIFICANT_CHANGE_INDEX,
     QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_RESET_SIGNIFICANT_CHANGE_INDEX,
+    /*EXT TDLS*/
+    QCA_NL80211_VENDOR_SUBCMD_TDLS_STATE_CHANGE_INDEX,
 };
 
 enum qca_wlan_vendor_attr
@@ -190,6 +197,64 @@
     QCA_WLAN_VENDOR_ATTR_MAX       = QCA_WLAN_VENDOR_ATTR_AFTER_LAST - 1,
 };
 
+
+/*EXT TDLS*/
+enum qca_wlan_vendor_attr_tdls_enable
+{
+    QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_INVALID = 0,
+    /* An array of 6 x Unsigned 8-bit value */
+    QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MAC_ADDR,
+    /* signed 32-bit value, but lets keep as unsigned for now */
+    QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_CHANNEL,
+    QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_GLOBAL_OPERATING_CLASS,
+    QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MAX_LATENCY_MS,
+    QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MIN_BANDWIDTH_KBPS,
+    /* keep last */
+    QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_AFTER_LAST,
+    QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MAX =
+         QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_AFTER_LAST - 1,
+};
+
+enum qca_wlan_vendor_attr_tdls_disable
+{
+    QCA_WLAN_VENDOR_ATTR_TDLS_DISABLE_INVALID = 0,
+    /* An array of 6 x Unsigned 8-bit value */
+    QCA_WLAN_VENDOR_ATTR_TDLS_DISABLE_MAC_ADDR,
+    /* keep last */
+    QCA_WLAN_VENDOR_ATTR_TDLS_DISABLE_AFTER_LAST,
+    QCA_WLAN_VENDOR_ATTR_TDLS_DISABLE_MAX =
+       QCA_WLAN_VENDOR_ATTR_TDLS_DISABLE_AFTER_LAST - 1,
+};
+
+enum qca_wlan_vendor_attr_tdls_get_status
+{
+    QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_INVALID = 0,
+    /* An array of 6 x Unsigned 8-bit value */
+    QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_MAC_ADDR,
+    /* signed 32-bit value, but lets keep as unsigned for now */
+    QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_STATE,
+    QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_REASON,
+    /* keep last */
+    QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_AFTER_LAST,
+    QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_MAX =
+      QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_AFTER_LAST - 1,
+};
+
+enum qca_wlan_vendor_attr_tdls_state
+{
+    QCA_WLAN_VENDOR_ATTR_TDLS_STATE_INVALID = 0,
+    /* An array of 6 x Unsigned 8-bit value */
+    QCA_WLAN_VENDOR_ATTR_TDLS_STATE_MAC_ADDR,
+    /* signed 32-bit value, but lets keep as unsigned for now */
+    QCA_WLAN_VENDOR_ATTR_TDLS_NEW_STATE,
+    QCA_WLAN_VENDOR_ATTR_TDLS_STATE_REASON,
+    /* keep last */
+    QCA_WLAN_VENDOR_ATTR_TDLS_STATE_AFTER_LAST,
+    QCA_WLAN_VENDOR_ATTR_TDLS_STATE_MAX =
+        QCA_WLAN_VENDOR_ATTR_TDLS_STATE_AFTER_LAST - 1,
+};
+
+
 #ifdef WLAN_FEATURE_LINK_LAYER_STATS
 
 enum qca_wlan_vendor_attr_ll_stats_set
diff --git a/CORE/HDD/inc/wlan_hdd_tdls.h b/CORE/HDD/inc/wlan_hdd_tdls.h
index 0cc6bba..ec38dfc 100644
--- a/CORE/HDD/inc/wlan_hdd_tdls.h
+++ b/CORE/HDD/inc/wlan_hdd_tdls.h
@@ -112,6 +112,37 @@
     eTDLS_LINK_TEARING,
 } tTDLSLinkStatus;
 
+
+typedef enum {
+    eTDLS_LINK_SUCCESS,                              /* Success */
+    eTDLS_LINK_UNSPECIFIED           = -1,           /* Unspecified reason */
+    eTDLS_LINK_NOT_SUPPORTED         = -2,           /* Remote side doesn't support TDLS */
+    eTDLS_LINK_UNSUPPORTED_BAND      = -3,           /* Remote side doesn't support this band */
+    eTDLS_LINK_NOT_BENEFICIAL        = -4,           /* Going to AP is better than going direct */
+    eTDLS_LINK_DROPPED_BY_REMOTE     = -5            /* Remote side doesn't want it anymore */
+} tTDLSLinkReason;
+
+typedef struct {
+    int channel;                        /* channel hint, in channel number (NOT frequency ) */
+    int global_operating_class;         /* operating class to use */
+    int max_latency_ms;                 /* max latency that can be tolerated by apps */
+    int min_bandwidth_kbps;             /* bandwidth required by apps, in kilo bits per second */
+} tdls_req_params_t;
+
+typedef enum {
+    WIFI_TDLS_DISABLED,                 /* TDLS is not enabled, or is disabled now */
+    WIFI_TDLS_ENABLED,                  /* TDLS is enabled, but not yet tried */
+    WIFI_TDLS_TRYING,                   /* Direct link is being attempted (optional) */
+    WIFI_TDLS_ESTABLISHED,              /* Direct link is established */
+    WIFI_TDLS_ESTABLISHED_OFF_CHANNEL,  /* Direct link is established using MCC */
+    WIFI_TDLS_DROPPED,                  /* Direct link was established, but is now dropped */
+    WIFI_TDLS_FAILED                    /* Direct link failed */
+} tdls_state_t;
+
+typedef int (*cfg80211_exttdls_callback)(tANI_U8* mac,
+                                         tANI_S32 state,
+                                         tANI_S32 reason,
+                                         void *ctx);
 typedef struct {
     tANI_U16    period;
     tANI_U16    bytes;
@@ -174,6 +205,9 @@
     vos_timer_t     peerIdleTimer;
     vos_timer_t     initiatorWaitTimeoutTimer;
     tANI_BOOLEAN isForcedPeer;
+    /*EXT TDLS*/
+    tTDLSLinkReason reason;
+    cfg80211_exttdls_callback state_change_notification;
 } hddTdlsPeer_t;
 
 typedef struct {
@@ -209,9 +243,13 @@
 
 int wlan_hdd_tdls_set_cap(hdd_adapter_t *pAdapter, u8* mac, tTDLSCapType cap);
 
-void wlan_hdd_tdls_set_peer_link_status(hddTdlsPeer_t *curr_peer, tTDLSLinkStatus status);
-
-void wlan_hdd_tdls_set_link_status(hdd_adapter_t *pAdapter, u8* mac, tTDLSLinkStatus status);
+void wlan_hdd_tdls_set_peer_link_status(hddTdlsPeer_t *curr_peer,
+                                        tTDLSLinkStatus status,
+                                        tTDLSLinkReason reason);
+void wlan_hdd_tdls_set_link_status(hdd_adapter_t *pAdapter,
+                                   u8* mac,
+                                   tTDLSLinkStatus linkStatus,
+                                   tTDLSLinkReason reason);
 
 int wlan_hdd_tdls_recv_discovery_resp(hdd_adapter_t *pAdapter, u8 *mac);
 
@@ -283,10 +321,24 @@
                                  vos_timer_t *timer,
                                  v_U32_t expirationTime);
 void wlan_hdd_tdls_indicate_teardown(hdd_adapter_t *pAdapter,
-                                           hddTdlsPeer_t *curr_peer,
-                                           tANI_U16 reason);
+                                     hddTdlsPeer_t *curr_peer,
+                                     tANI_U16 reason);
 
 int wlan_hdd_tdls_set_force_peer(hdd_adapter_t *pAdapter, u8 *mac,
                                  tANI_BOOLEAN forcePeer);
+int wlan_hdd_tdls_extctrl_deconfig_peer(hdd_adapter_t *pAdapter, u8 *peer);
+int wlan_hdd_tdls_extctrl_config_peer(hdd_adapter_t *pAdapter,
+                                      u8 *peer,
+                                      cfg80211_exttdls_callback callback);
+/*EXT TDLS*/
+int wlan_hdd_tdls_get_status(hdd_adapter_t *pAdapter,
+                             tANI_U8* mac,
+                             tANI_S32 *state,
+                             tANI_S32 *reason);
+void wlan_hdd_tdls_get_wifi_hal_state(hddTdlsPeer_t *curr_peer,
+                                      tANI_S32 *state,
+                                      tANI_S32 *reason);
+int wlan_hdd_set_callback(hddTdlsPeer_t *curr_peer,
+                          cfg80211_exttdls_callback callback);
 
 #endif // __HDD_TDSL_H
diff --git a/CORE/HDD/src/wlan_hdd_cfg80211.c b/CORE/HDD/src/wlan_hdd_cfg80211.c
index 6dc3b26..f14b1f8 100644
--- a/CORE/HDD/src/wlan_hdd_cfg80211.c
+++ b/CORE/HDD/src/wlan_hdd_cfg80211.c
@@ -166,6 +166,16 @@
 #define EXTSCAN_MAX_CACHED_RESULTS_PER_IND 32
 #endif
 
+/*EXT TDLS*/
+/*
+ * Used to allocate the size of 4096 for the TDLS.
+ * The size of 4096 is considered assuming that all data per
+ * respective event fit with in the limit.Please take a call
+ * on the limit based on the data requirements on link layer
+ * statistics.
+ */
+#define EXTTDLS_EVENT_BUF_SIZE 4096
+
 static const u32 hdd_cipher_suites[] =
 {
     WLAN_CIPHER_SUITE_WEP40,
@@ -3839,6 +3849,303 @@
 
 #endif /* WLAN_FEATURE_EXTSCAN */
 
+/*EXT TDLS*/
+static const struct nla_policy
+wlan_hdd_tdls_config_enable_policy[QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MAX +1] =
+{
+    [QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MAC_ADDR] = {.type = NLA_UNSPEC },
+    [QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_CHANNEL] = {.type = NLA_S32 },
+    [QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_GLOBAL_OPERATING_CLASS] =
+                                                       {.type = NLA_S32 },
+    [QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MAX_LATENCY_MS] = {.type = NLA_S32 },
+    [QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MIN_BANDWIDTH_KBPS] = {.type = NLA_S32 },
+
+};
+
+static const struct nla_policy
+wlan_hdd_tdls_config_disable_policy[QCA_WLAN_VENDOR_ATTR_TDLS_DISABLE_MAX +1] =
+{
+    [QCA_WLAN_VENDOR_ATTR_TDLS_DISABLE_MAC_ADDR] = {.type = NLA_UNSPEC },
+
+};
+
+static const struct nla_policy
+wlan_hdd_tdls_config_state_change_policy[
+                    QCA_WLAN_VENDOR_ATTR_TDLS_STATE_MAX +1] =
+{
+    [QCA_WLAN_VENDOR_ATTR_TDLS_STATE_MAC_ADDR] = {.type = NLA_UNSPEC },
+    [QCA_WLAN_VENDOR_ATTR_TDLS_NEW_STATE] = {.type = NLA_S32 },
+    [QCA_WLAN_VENDOR_ATTR_TDLS_STATE_REASON] = {.type = NLA_S32 },
+
+};
+
+static const struct nla_policy
+wlan_hdd_tdls_config_get_status_policy[
+                     QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_MAX +1] =
+{
+    [QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_MAC_ADDR] = {.type = NLA_UNSPEC },
+    [QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_STATE] = {.type = NLA_S32 },
+    [QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_REASON] = {.type = NLA_S32 },
+
+};
+static int wlan_hdd_cfg80211_exttdls_get_status(struct wiphy *wiphy,
+                                                struct wireless_dev *wdev,
+                                                void *data,
+                                                int data_len)
+{
+    u8 peer[6]                                 = {0};
+    struct net_device *dev                     = wdev->netdev;
+    hdd_adapter_t *pAdapter                    = WLAN_HDD_GET_PRIV_PTR(dev);
+    hdd_context_t *pHddCtx                     = wiphy_priv(wiphy);
+    struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_MAX + 1];
+    eHalStatus ret;
+    tANI_S32 state;
+    tANI_S32 reason;
+    struct sk_buff *skb         = NULL;
+
+    ret = wlan_hdd_validate_context(pHddCtx);
+    if (0 != ret) {
+
+        return -EINVAL;
+    }
+    if (pHddCtx->cfg_ini->fTDLSExternalControl == FALSE) {
+
+        return -ENOTSUPP;
+    }
+    if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_MAX,
+                    data, data_len,
+                    wlan_hdd_tdls_config_get_status_policy)) {
+        hddLog(VOS_TRACE_LEVEL_ERROR, FL("Invalid ATTR"));
+        return -EINVAL;
+    }
+
+    /* Parse and fetch mac address */
+    if (!tb[QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_MAC_ADDR]) {
+        hddLog(VOS_TRACE_LEVEL_ERROR, FL("attr mac addr failed"));
+        return -EINVAL;
+    }
+
+    memcpy(peer, nla_data(
+           tb[QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_MAC_ADDR]),
+           sizeof(peer));
+    hddLog(VOS_TRACE_LEVEL_INFO, FL(MAC_ADDRESS_STR),MAC_ADDR_ARRAY(peer));
+
+    ret = wlan_hdd_tdls_get_status(pAdapter, peer, &state, &reason);
+
+    if (0 != ret) {
+        hddLog(VOS_TRACE_LEVEL_ERROR,
+               FL("get status Failed"));
+        return -EINVAL;
+    }
+    skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy,
+                                              2 * sizeof(s32) +
+                                              NLMSG_HDRLEN);
+
+    if (!skb) {
+        hddLog(VOS_TRACE_LEVEL_ERROR,
+                  FL("cfg80211_vendor_cmd_alloc_reply_skb failed"));
+        return -EINVAL;
+    }
+    hddLog(VOS_TRACE_LEVEL_INFO, FL("Reason (%d) Status (%d) tdls peer" MAC_ADDRESS_STR),
+                                 reason,
+                                 state,
+                                 MAC_ADDR_ARRAY(peer));
+
+    if (nla_put_s32(skb, QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_STATE, state) ||
+        nla_put_s32(skb, QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_REASON, reason)) {
+
+        hddLog(VOS_TRACE_LEVEL_ERROR, FL("nla put fail"));
+        goto nla_put_failure;
+    }
+
+    return cfg80211_vendor_cmd_reply(skb);
+
+nla_put_failure:
+    kfree_skb(skb);
+    return -EINVAL;
+}
+
+static int wlan_hdd_cfg80211_exttdls_callback(tANI_U8* mac,
+                                              tANI_S32 state,
+                                              tANI_S32 reason,
+                                              void *ctx)
+{
+    hdd_adapter_t* pAdapter       = (hdd_adapter_t*)ctx;
+    hdd_context_t *pHddCtx        = WLAN_HDD_GET_CTX(pAdapter);
+    struct sk_buff *skb           = NULL;
+
+    if (wlan_hdd_validate_context(pHddCtx)) {
+        hddLog(VOS_TRACE_LEVEL_ERROR, FL("HDD context is not valid "));
+        return -EINVAL;
+    }
+
+    if (pHddCtx->cfg_ini->fTDLSExternalControl == FALSE) {
+
+        return -ENOTSUPP;
+    }
+    skb = cfg80211_vendor_event_alloc(
+                            pHddCtx->wiphy,
+                            EXTTDLS_EVENT_BUF_SIZE + NLMSG_HDRLEN,
+                            QCA_NL80211_VENDOR_SUBCMD_TDLS_STATE_CHANGE_INDEX,
+                            GFP_KERNEL);
+
+    if (!skb) {
+        hddLog(VOS_TRACE_LEVEL_ERROR,
+                  FL("cfg80211_vendor_event_alloc failed"));
+        return -EINVAL;
+    }
+    hddLog(VOS_TRACE_LEVEL_INFO, FL("Entering "));
+    hddLog(VOS_TRACE_LEVEL_INFO, "Reason (%d) Status (%d)", reason, state);
+    hddLog(VOS_TRACE_LEVEL_WARN, "tdls peer " MAC_ADDRESS_STR,
+                                          MAC_ADDR_ARRAY(mac));
+
+    if (nla_put(skb, QCA_WLAN_VENDOR_ATTR_TDLS_STATE_MAC_ADDR,
+                                              VOS_MAC_ADDR_SIZE, mac) ||
+        nla_put_s32(skb, QCA_WLAN_VENDOR_ATTR_TDLS_NEW_STATE, state) ||
+        nla_put_s32(skb, QCA_WLAN_VENDOR_ATTR_TDLS_STATE_REASON, reason)
+       ) {
+
+        hddLog(VOS_TRACE_LEVEL_ERROR, FL("nla put fail"));
+        goto nla_put_failure;
+    }
+
+    cfg80211_vendor_event(skb, GFP_KERNEL);
+    return (0);
+
+nla_put_failure:
+    kfree_skb(skb);
+    return -EINVAL;
+}
+
+static int wlan_hdd_cfg80211_exttdls_enable(struct wiphy *wiphy,
+                                            struct wireless_dev *wdev,
+                                            void *data,
+                                            int data_len)
+{
+    u8 peer[6]                                 = {0};
+    struct net_device *dev                     = wdev->netdev;
+    hdd_adapter_t *pAdapter                    = WLAN_HDD_GET_PRIV_PTR(dev);
+    hdd_context_t *pHddCtx                     = wiphy_priv(wiphy);
+    struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MAX + 1];
+    eHalStatus status;
+    tdls_req_params_t   pReqMsg = {0};
+
+    status = wlan_hdd_validate_context(pHddCtx);
+    if (0 != status) {
+        hddLog(VOS_TRACE_LEVEL_ERROR,
+               FL("HDD context is not valid"));
+        return -EINVAL;
+    }
+    if (pHddCtx->cfg_ini->fTDLSExternalControl == FALSE) {
+
+        return -ENOTSUPP;
+    }
+    if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MAX,
+                    data, data_len,
+                    wlan_hdd_tdls_config_enable_policy)) {
+        hddLog(VOS_TRACE_LEVEL_ERROR, FL("Invalid ATTR"));
+        return -EINVAL;
+    }
+
+    /* Parse and fetch mac address */
+    if (!tb[QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MAC_ADDR]) {
+        hddLog(VOS_TRACE_LEVEL_ERROR, FL("attr mac addr failed"));
+        return -EINVAL;
+    }
+
+    memcpy(peer, nla_data(
+                tb[QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MAC_ADDR]),
+                sizeof(peer));
+    hddLog(VOS_TRACE_LEVEL_INFO, FL(MAC_ADDRESS_STR),MAC_ADDR_ARRAY(peer));
+
+    /* Parse and fetch channel */
+    if (!tb[QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_CHANNEL]) {
+        hddLog(VOS_TRACE_LEVEL_ERROR, FL("attr channel failed"));
+        return -EINVAL;
+    }
+    pReqMsg.channel = nla_get_s32(
+         tb[QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_CHANNEL]);
+    hddLog(VOS_TRACE_LEVEL_INFO, FL("Channel Num (%d)"), pReqMsg.channel);
+
+    /* Parse and fetch global operating class */
+    if (!tb[QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_GLOBAL_OPERATING_CLASS]) {
+        hddLog(VOS_TRACE_LEVEL_ERROR, FL("attr operating class failed"));
+        return -EINVAL;
+    }
+    pReqMsg.global_operating_class = nla_get_s32(
+        tb[QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_GLOBAL_OPERATING_CLASS]);
+    hddLog(VOS_TRACE_LEVEL_INFO, FL("Operating class (%d)"),
+                                     pReqMsg.global_operating_class);
+
+    /* Parse and fetch latency ms */
+    if (!tb[QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MAX_LATENCY_MS]) {
+        hddLog(VOS_TRACE_LEVEL_ERROR, FL("attr latency failed"));
+        return -EINVAL;
+    }
+    pReqMsg.max_latency_ms = nla_get_s32(
+        tb[QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MAX_LATENCY_MS]);
+    hddLog(VOS_TRACE_LEVEL_INFO, FL("Latency (%d)"),
+                                     pReqMsg.max_latency_ms);
+
+    /* Parse and fetch required bandwidth kbps */
+    if (!tb[QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MIN_BANDWIDTH_KBPS]) {
+        hddLog(VOS_TRACE_LEVEL_ERROR, FL("attr bandwidth failed"));
+        return -EINVAL;
+    }
+
+    pReqMsg.min_bandwidth_kbps = nla_get_s32(
+        tb[QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MIN_BANDWIDTH_KBPS]);
+    hddLog(VOS_TRACE_LEVEL_INFO, FL("Bandwidth (%d)"),
+                                     pReqMsg.min_bandwidth_kbps);
+
+    return (wlan_hdd_tdls_extctrl_config_peer(pAdapter,
+                                 peer,
+                                 wlan_hdd_cfg80211_exttdls_callback));
+}
+
+static int wlan_hdd_cfg80211_exttdls_disable(struct wiphy *wiphy,
+                                             struct wireless_dev *wdev,
+                                             void *data,
+                                             int data_len)
+{
+    u8 peer[6]                                 = {0};
+    struct net_device *dev                     = wdev->netdev;
+    hdd_adapter_t *pAdapter                    = WLAN_HDD_GET_PRIV_PTR(dev);
+    hdd_context_t *pHddCtx                     = wiphy_priv(wiphy);
+    struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_TDLS_DISABLE_MAX + 1];
+    eHalStatus status;
+
+    status = wlan_hdd_validate_context(pHddCtx);
+    if (0 != status) {
+        hddLog(VOS_TRACE_LEVEL_ERROR,
+               FL("HDD context is not valid"));
+        return -EINVAL;
+    }
+    if (pHddCtx->cfg_ini->fTDLSExternalControl == FALSE) {
+
+        return -ENOTSUPP;
+    }
+    if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_TDLS_DISABLE_MAX,
+                    data, data_len,
+                    wlan_hdd_tdls_config_disable_policy)) {
+        hddLog(VOS_TRACE_LEVEL_ERROR, FL("Invalid ATTR"));
+        return -EINVAL;
+    }
+    /* Parse and fetch mac address */
+    if (!tb[QCA_WLAN_VENDOR_ATTR_TDLS_DISABLE_MAC_ADDR]) {
+        hddLog(VOS_TRACE_LEVEL_ERROR, FL("attr mac addr failed"));
+        return -EINVAL;
+    }
+
+    memcpy(peer, nla_data(
+                tb[QCA_WLAN_VENDOR_ATTR_TDLS_DISABLE_MAC_ADDR]),
+                sizeof(peer));
+    hddLog(VOS_TRACE_LEVEL_INFO, FL(MAC_ADDRESS_STR),MAC_ADDR_ARRAY(peer));
+
+    return (wlan_hdd_tdls_extctrl_deconfig_peer(pAdapter, peer));
+}
+
+
 const struct wiphy_vendor_command hdd_wiphy_vendor_commands[] =
 {
 #ifdef WLAN_FEATURE_LINK_LAYER_STATS
@@ -3942,6 +4249,31 @@
         .doit = wlan_hdd_cfg80211_extscan_reset_significant_change
     },
 #endif /* WLAN_FEATURE_EXTSCAN */
+/*EXT TDLS*/
+    {
+        .info.vendor_id = QCA_NL80211_VENDOR_ID,
+        .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_TDLS_ENABLE,
+        .flags = WIPHY_VENDOR_CMD_NEED_WDEV |
+                 WIPHY_VENDOR_CMD_NEED_NETDEV |
+                 WIPHY_VENDOR_CMD_NEED_RUNNING,
+        .doit = wlan_hdd_cfg80211_exttdls_enable
+    },
+    {
+        .info.vendor_id = QCA_NL80211_VENDOR_ID,
+        .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_TDLS_DISABLE,
+        .flags = WIPHY_VENDOR_CMD_NEED_WDEV |
+                 WIPHY_VENDOR_CMD_NEED_NETDEV |
+                 WIPHY_VENDOR_CMD_NEED_RUNNING,
+        .doit = wlan_hdd_cfg80211_exttdls_disable
+    },
+    {
+        .info.vendor_id = QCA_NL80211_VENDOR_ID,
+        .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_TDLS_GET_STATUS,
+        .flags = WIPHY_VENDOR_CMD_NEED_WDEV |
+                 WIPHY_VENDOR_CMD_NEED_NETDEV,
+        .doit = wlan_hdd_cfg80211_exttdls_get_status
+    },
+
 };
 
 /* vendor specific events */
@@ -4040,7 +4372,11 @@
         .subcmd = QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_RESET_SIGNIFICANT_CHANGE
     },
 #endif /* WLAN_FEATURE_EXTSCAN */
-
+/*EXT TDLS*/
+    {
+        .vendor_id = QCA_NL80211_VENDOR_ID,
+        .subcmd = QCA_NL80211_VENDOR_SUBCMD_TDLS_STATE
+    },
 };
 
 /*
@@ -6773,7 +7109,10 @@
     {
         VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
                 "%s:LOGP in Progress. Ignore!!!", __func__);
-        wlan_hdd_tdls_set_link_status(pAdapter, mac, eTDLS_LINK_IDLE);
+        wlan_hdd_tdls_set_link_status(pAdapter,
+                                      mac,
+                                      eTDLS_LINK_IDLE,
+                                      eTDLS_LINK_UNSPECIFIED);
         return -EBUSY;
     }
 
@@ -6847,7 +7186,10 @@
         }
     }
     if (0 == update)
-        wlan_hdd_tdls_set_link_status(pAdapter, mac, eTDLS_LINK_CONNECTING);
+        wlan_hdd_tdls_set_link_status(pAdapter,
+                                      mac,
+                                      eTDLS_LINK_CONNECTING,
+                                      eTDLS_LINK_SUCCESS);
 
     /* debug code */
     if (NULL != StaParams)
@@ -6922,7 +7264,10 @@
     return 0;
 
 error:
-    wlan_hdd_tdls_set_link_status(pAdapter, mac, eTDLS_LINK_IDLE);
+    wlan_hdd_tdls_set_link_status(pAdapter,
+                                  mac,
+                                  eTDLS_LINK_IDLE,
+                                  eTDLS_LINK_UNSPECIFIED);
     return -EPERM;
 
 }
@@ -12769,7 +13114,10 @@
     {
         VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
                 "%s:LOGP in Progress. Ignore!!!", __func__);
-        wlan_hdd_tdls_set_link_status(pAdapter, peer, eTDLS_LINK_IDLE);
+        wlan_hdd_tdls_set_link_status(pAdapter,
+                                      peer,
+                                      eTDLS_LINK_IDLE,
+                                      eTDLS_LINK_UNSPECIFIED);
         return -EBUSY;
     }
 
@@ -12970,6 +13318,105 @@
     return 0;
 }
 
+
+int wlan_hdd_tdls_extctrl_config_peer(hdd_adapter_t *pAdapter,
+                                      u8 *peer,
+                                      cfg80211_exttdls_callback callback)
+{
+
+    hddTdlsPeer_t *pTdlsPeer;
+    hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
+    VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
+              " %s : NL80211_TDLS_SETUP for " MAC_ADDRESS_STR,
+              __func__, MAC_ADDR_ARRAY(peer));
+
+    if ( (FALSE == pHddCtx->cfg_ini->fTDLSExternalControl) ||
+         (FALSE == pHddCtx->cfg_ini->fEnableTDLSImplicitTrigger) ) {
+
+        VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
+              " %s TDLS External control and Implicit Trigger not enabled ",
+              __func__);
+        return -ENOTSUPP;
+    }
+
+    /* To cater the requirement of establishing the TDLS link
+     * irrespective of the data traffic , get an entry of TDLS peer.
+     */
+    pTdlsPeer = wlan_hdd_tdls_get_peer(pAdapter, peer);
+    if (pTdlsPeer == NULL) {
+        VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
+                  "%s: peer " MAC_ADDRESS_STR " not existing",
+                  __func__, MAC_ADDR_ARRAY(peer));
+        return -EINVAL;
+    }
+
+    if ( 0 != wlan_hdd_tdls_set_force_peer(pAdapter, peer, TRUE) ) {
+
+        VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
+              " %s TDLS Add Force Peer Failed",
+              __func__);
+        return -EINVAL;
+    }
+    /*EXT TDLS*/
+
+    if ( 0 != wlan_hdd_set_callback(pTdlsPeer, callback) ) {
+        VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
+              " %s TDLS set callback Failed",
+              __func__);
+        return -EINVAL;
+    }
+
+    return(0);
+
+}
+
+int wlan_hdd_tdls_extctrl_deconfig_peer(hdd_adapter_t *pAdapter, u8 *peer)
+{
+
+    hddTdlsPeer_t *pTdlsPeer;
+    hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
+    VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
+              " %s : NL80211_TDLS_TEARDOWN for " MAC_ADDRESS_STR,
+              __func__, MAC_ADDR_ARRAY(peer));
+
+    if ( (FALSE == pHddCtx->cfg_ini->fTDLSExternalControl) ||
+         (FALSE == pHddCtx->cfg_ini->fEnableTDLSImplicitTrigger) ) {
+
+        VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
+              " %s TDLS External control and Implicit Trigger not enabled ",
+              __func__);
+        return -ENOTSUPP;
+    }
+
+
+    pTdlsPeer = wlan_hdd_tdls_find_peer(pAdapter, peer, TRUE);
+
+    if ( NULL == pTdlsPeer ) {
+        hddLog(VOS_TRACE_LEVEL_INFO, "%s: " MAC_ADDRESS_STR
+               " peer not exsting",
+               __func__, MAC_ADDR_ARRAY(peer));
+        return -EINVAL;
+    }
+    else {
+        wlan_hdd_tdls_indicate_teardown(pAdapter, pTdlsPeer,
+                           eSIR_MAC_TDLS_TEARDOWN_UNSPEC_REASON);
+    }
+
+    if ( 0 != wlan_hdd_tdls_set_force_peer(pAdapter, peer, FALSE) )
+        return -EINVAL;
+
+    /*EXT TDLS*/
+
+    if ( 0 != wlan_hdd_set_callback(pTdlsPeer, NULL )) {
+
+        VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
+              " %s TDLS set callback Failed",
+              __func__);
+        return -EINVAL;
+    }
+    return(0);
+
+}
 static int __wlan_hdd_cfg80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
                  u8 *peer, enum nl80211_tdls_operation oper)
 {
@@ -13063,7 +13510,9 @@
                             return -EINVAL;
                         }
                     }
-                    wlan_hdd_tdls_set_peer_link_status(pTdlsPeer, eTDLS_LINK_CONNECTED);
+                    wlan_hdd_tdls_set_peer_link_status(pTdlsPeer,
+                                                       eTDLS_LINK_CONNECTED,
+                                                       eTDLS_LINK_SUCCESS);
                     /* Mark TDLS client Authenticated .*/
                     status = WLANTL_ChangeSTAState( pHddCtx->pvosContext,
                                                     pTdlsPeer->staId,
@@ -13137,13 +13586,17 @@
                               msecs_to_jiffies(WAIT_TIME_TDLS_DEL_STA));
                     if (status <= 0)
                     {
-                        wlan_hdd_tdls_set_peer_link_status(pTdlsPeer, eTDLS_LINK_IDLE);
+                        wlan_hdd_tdls_set_peer_link_status(pTdlsPeer,
+                                                           eTDLS_LINK_IDLE,
+                                                           eTDLS_LINK_UNSPECIFIED);
                         VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
                                   "%s: Del station failed status %ld",
                                   __func__, status);
                         return -EPERM;
                     }
-                    wlan_hdd_tdls_set_peer_link_status(pTdlsPeer, eTDLS_LINK_IDLE);
+                    wlan_hdd_tdls_set_peer_link_status(pTdlsPeer,
+                                                       eTDLS_LINK_IDLE,
+                                                       eTDLS_LINK_UNSPECIFIED);
                 }
                 else
                 {
@@ -13154,70 +13607,27 @@
             break;
         case NL80211_TDLS_TEARDOWN:
             {
-                VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
-                          " %s : NL80211_TDLS_TEARDOWN for " MAC_ADDRESS_STR,
-                          __func__, MAC_ADDR_ARRAY(peer));
+                status = wlan_hdd_tdls_extctrl_deconfig_peer(pAdapter, peer);
 
-                if ( (FALSE == pHddCtx->cfg_ini->fTDLSExternalControl) ||
-                     (FALSE == pHddCtx->cfg_ini->fEnableTDLSImplicitTrigger) ) {
-
-                    VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
-                          " %s TDLS External control and Implicit Trigger not enabled ",
-                          __func__);
-                    return -ENOTSUPP;
+                if (0 != status)
+                {
+                    VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
+                               "%s: Error in TDLS Teardown", __func__);
+                    return status;
                 }
-
-
-                pTdlsPeer = wlan_hdd_tdls_find_peer(pAdapter, peer, TRUE);
-
-                if ( NULL == pTdlsPeer ) {
-                    hddLog(VOS_TRACE_LEVEL_INFO, "%s: " MAC_ADDRESS_STR
-                           " peer not exsting",
-                           __func__, MAC_ADDR_ARRAY(peer));
-                    return -EINVAL;
-                }
-                else {
-                    wlan_hdd_tdls_indicate_teardown(pAdapter, pTdlsPeer,
-                                       eSIR_MAC_TDLS_TEARDOWN_UNSPEC_REASON);
-                }
-
-                if ( 0 != wlan_hdd_tdls_set_force_peer(pAdapter, peer, FALSE) )
-                    return -EINVAL;
                 break;
             }
         case NL80211_TDLS_SETUP:
             {
-                hddTdlsPeer_t *pTdlsPeer;
-                VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
-                          " %s : NL80211_TDLS_SETUP for " MAC_ADDRESS_STR,
-                          __func__, MAC_ADDR_ARRAY(peer));
+                status = wlan_hdd_tdls_extctrl_config_peer(pAdapter,
+                                                           peer,
+                                                           NULL);
 
-                if ( (FALSE == pHddCtx->cfg_ini->fTDLSExternalControl) ||
-                     (FALSE == pHddCtx->cfg_ini->fEnableTDLSImplicitTrigger) ) {
-
-                    VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
-                          " %s TDLS External control and Implicit Trigger not enabled ",
-                          __func__);
-                    return -ENOTSUPP;
-                }
-
-                /* To cater the requirement of establishing the TDLS link
-                 * irrespective of the data traffic , get an entry of TDLS peer.
-                 */
-                pTdlsPeer = wlan_hdd_tdls_get_peer(pAdapter, peer);
-                if (pTdlsPeer == NULL) {
-                    VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
-                              "%s: peer " MAC_ADDRESS_STR " not existing",
-                              __func__, MAC_ADDR_ARRAY(peer));
-                    return -EINVAL;
-                }
-
-                if ( 0 != wlan_hdd_tdls_set_force_peer(pAdapter, peer, TRUE) ) {
-
-                    VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
-                          " %s TDLS Add Force Peer Failed",
-                          __func__);
-                    return -EINVAL;
+                if (0 != status)
+                {
+                    VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
+                               "%s: Error in TDLS Setup", __func__);
+                    return status;
                 }
                 break;
             }
diff --git a/CORE/HDD/src/wlan_hdd_tdls.c b/CORE/HDD/src/wlan_hdd_tdls.c
index e228607..d6bd7f0 100644
--- a/CORE/HDD/src/wlan_hdd_tdls.c
+++ b/CORE/HDD/src/wlan_hdd_tdls.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012-2013 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2014 The Linux Foundation. All rights reserved.
  *
  * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
  *
@@ -406,7 +406,11 @@
                         else
                         {
                             curr_peer->tdls_support = eTDLS_CAP_NOT_SUPPORTED;
-                            curr_peer->link_status  = eTDLS_LINK_IDLE;
+                            wlan_hdd_tdls_set_peer_link_status(
+                                                    curr_peer,
+                                                    eTDLS_LINK_IDLE,
+                                                    eTDLS_LINK_NOT_SUPPORTED);
+
                         }
                     }
                 }
@@ -527,7 +531,10 @@
                 VOS_TRACE( VOS_MODULE_ID_HDD, TDLS_LOG_LEVEL,
                            "%s: " MAC_ADDRESS_STR " to idle state", __func__,
                            MAC_ADDR_ARRAY(tmp->peerMac));
-                tmp->link_status = eTDLS_LINK_IDLE;
+                           wlan_hdd_tdls_set_peer_link_status(
+                                                  tmp,
+                                                  eTDLS_LINK_IDLE,
+                                                  eTDLS_LINK_UNSPECIFIED);
             }
         }
     }
@@ -967,8 +974,14 @@
     return 0;
 }
 
-void wlan_hdd_tdls_set_peer_link_status(hddTdlsPeer_t *curr_peer, tTDLSLinkStatus status)
+void wlan_hdd_tdls_set_peer_link_status(hddTdlsPeer_t *curr_peer,
+                                        tTDLSLinkStatus status,
+                                        tTDLSLinkReason reason)
 {
+    /*EXT TDLS*/
+    tANI_S32 state = 0;
+    tANI_S32 res = 0;
+    /*EXT TDLS*/
     if (curr_peer == NULL)
     {
        VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
@@ -981,12 +994,34 @@
 
     curr_peer->link_status = status;
 
+    /*EXT TDLS*/
+    if (curr_peer->isForcedPeer && curr_peer->state_change_notification)
+    {
+        /*save the reason for any further query*/
+        curr_peer->reason = reason;
+        wlan_hdd_tdls_get_wifi_hal_state(curr_peer, &state, &res);
+
+        (curr_peer->state_change_notification)(
+                                          curr_peer->peerMac,
+                                          state,
+                                          res,
+                                          curr_peer->pHddTdlsCtx->pAdapter);
+
+    }
+    /*EXT TDLS*/
+
 }
 
 void wlan_hdd_tdls_set_link_status(hdd_adapter_t *pAdapter,
                                    u8* mac,
-                                   tTDLSLinkStatus linkStatus)
+                                   tTDLSLinkStatus linkStatus,
+                                   tTDLSLinkReason reason)
 {
+
+    /*EXT TDLS*/
+    tANI_S32 state = 0;
+    tANI_S32 res = 0;
+    /*EXT TDLS*/
     hddTdlsPeer_t *curr_peer;
 
     curr_peer = wlan_hdd_tdls_find_peer(pAdapter, mac, TRUE);
@@ -999,6 +1034,21 @@
 
     curr_peer->link_status= linkStatus;
 
+    /*EXT TDLS*/
+
+    if (curr_peer->isForcedPeer && curr_peer->state_change_notification)
+    {
+        /*save the reason for any further query*/
+        curr_peer->reason = reason;
+        wlan_hdd_tdls_get_wifi_hal_state(curr_peer, &state, &res);
+
+        (*curr_peer->state_change_notification)(mac,
+                                             state,
+                                             res,
+                                             curr_peer->pHddTdlsCtx->pAdapter);
+
+    }
+    /*EXT TDLS*/
     return;
 }
 
@@ -1057,7 +1107,9 @@
            threshold is also met before setting up TDLS link*/
         if ((tANI_S32) curr_peer->rssi > (tANI_S32) pHddTdlsCtx->threshold_config.rssi_trigger_threshold)
         {
-            curr_peer->link_status = eTDLS_LINK_DISCOVERED;
+            wlan_hdd_tdls_set_peer_link_status(curr_peer,
+                                               eTDLS_LINK_DISCOVERED,
+                                               eTDLS_LINK_SUCCESS);
             VOS_TRACE( VOS_MODULE_ID_HDD, TDLS_LOG_LEVEL,
             "Rssi Threshold met: "MAC_ADDRESS_STR" rssi = %d threshold= %d" ,
              MAC_ADDR_ARRAY(curr_peer->peerMac), curr_peer->rssi,
@@ -1070,7 +1122,9 @@
             "Rssi Threshold not met: "MAC_ADDRESS_STR" rssi = %d threshold = %d ",
             MAC_ADDR_ARRAY(curr_peer->peerMac), curr_peer->rssi,
             pHddTdlsCtx->threshold_config.rssi_trigger_threshold);
-            curr_peer->link_status = eTDLS_LINK_IDLE;
+            wlan_hdd_tdls_set_peer_link_status(curr_peer,
+                                               eTDLS_LINK_IDLE,
+                                               eTDLS_LINK_UNSPECIFIED);
         }
     }
     else
@@ -1516,7 +1570,9 @@
         return -1;
     }
 
-    curr_peer->link_status = eTDLS_LINK_IDLE;
+    wlan_hdd_tdls_set_peer_link_status(curr_peer,
+                                       eTDLS_LINK_IDLE,
+                                       eTDLS_LINK_UNSPECIFIED);
     curr_peer->staId = 0;
 
     /* Throughput Monitor shall disable the split scan when
@@ -2110,7 +2166,9 @@
     }
 
     if (eTDLS_CAP_UNKNOWN != curr_peer->tdls_support)
-        curr_peer->link_status = eTDLS_LINK_DISCOVERING;
+        wlan_hdd_tdls_set_peer_link_status(curr_peer,
+                                           eTDLS_LINK_DISCOVERING,
+                                           eTDLS_LINK_SUCCESS);
 
     status = wlan_hdd_cfg80211_send_tdls_discover_req(pHddTdlsCtx->pAdapter->wdev.wiphy,
                                             pHddTdlsCtx->pAdapter->dev,
@@ -2121,7 +2179,9 @@
         VOS_TRACE( VOS_MODULE_ID_HDD, TDLS_LOG_LEVEL, "%s: " MAC_ADDRESS_STR " discovery could not sent",
             __func__, MAC_ADDR_ARRAY(curr_peer->peerMac));
         if (eTDLS_CAP_UNKNOWN != curr_peer->tdls_support)
-            curr_peer->link_status = eTDLS_LINK_IDLE;
+            wlan_hdd_tdls_set_peer_link_status(curr_peer,
+                                               eTDLS_LINK_IDLE,
+                                               eTDLS_LINK_UNSPECIFIED);
         goto done;
     }
 
@@ -2293,7 +2353,9 @@
                     "%s: " MAC_ADDRESS_STR ". scan rejected %d. force it to idle",
                     __func__, MAC_ADDR_ARRAY (curr_peer->peerMac), pHddCtx->tdls_scan_ctxt.reject);
 
-            wlan_hdd_tdls_set_peer_link_status (curr_peer, eTDLS_LINK_IDLE);
+            wlan_hdd_tdls_set_peer_link_status (curr_peer,
+                                                eTDLS_LINK_IDLE,
+                                                eTDLS_LINK_UNSPECIFIED);
             return 1;
         }
         VOS_TRACE(VOS_MODULE_ID_HDD, TDLS_LOG_LEVEL,
@@ -2523,10 +2585,86 @@
         pHddCtx->isTdlsScanCoexistence = FALSE;
     }
 
-    wlan_hdd_tdls_set_peer_link_status(curr_peer, eTDLS_LINK_TEARING);
+    wlan_hdd_tdls_set_peer_link_status(curr_peer,
+                                       eTDLS_LINK_TEARING,
+                                       eTDLS_LINK_DROPPED_BY_REMOTE);
     cfg80211_tdls_oper_request(pAdapter->dev,
                                curr_peer->peerMac,
                                NL80211_TDLS_TEARDOWN,
                                reason,
                                GFP_KERNEL);
 }
+
+
+/*EXT TDLS*/
+int wlan_hdd_set_callback(hddTdlsPeer_t *curr_peer,
+                         cfg80211_exttdls_callback callback)
+{
+
+    hdd_context_t *pHddCtx;
+    hdd_adapter_t   *pAdapter;
+
+    if (!curr_peer) return -1;
+
+    pAdapter = curr_peer->pHddTdlsCtx->pAdapter;
+    pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
+    if ((NULL == pHddCtx)) return -1;
+
+    mutex_lock(&pHddCtx->tdls_lock);
+
+    curr_peer->state_change_notification = callback;
+
+    mutex_unlock(&pHddCtx->tdls_lock);
+    return 0;
+
+
+}
+
+void wlan_hdd_tdls_get_wifi_hal_state(hddTdlsPeer_t *curr_peer,
+                                      tANI_S32 *state,
+                                      tANI_S32 *reason)
+{
+    *reason = curr_peer->reason;
+
+    switch(curr_peer->link_status)
+    {
+        case eTDLS_LINK_IDLE:
+        case eTDLS_LINK_DISCOVERING:
+        case eTDLS_LINK_DISCOVERED:
+            *state = WIFI_TDLS_ENABLED;
+            break;
+        case eTDLS_LINK_CONNECTING:
+            *state = WIFI_TDLS_TRYING;
+            break;
+        case eTDLS_LINK_CONNECTED:
+            *state = WIFI_TDLS_ESTABLISHED;
+            break;
+        case eTDLS_LINK_TEARING:
+            *state = WIFI_TDLS_DROPPED;
+            break;
+    }
+
+}
+
+int wlan_hdd_tdls_get_status(hdd_adapter_t *pAdapter,
+                             tANI_U8* mac,
+                             tANI_S32 *state,
+                             tANI_S32 *reason)
+{
+
+    hddTdlsPeer_t *curr_peer;
+
+    curr_peer = wlan_hdd_tdls_find_peer(pAdapter, mac, TRUE);
+    if (curr_peer == NULL)
+    {
+       VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
+                 FL("curr_peer is NULL"));
+       return -EINVAL;
+    }
+
+    wlan_hdd_tdls_get_wifi_hal_state(curr_peer, state, reason);
+    return (0);
+}
+
+/*EXT TDLS*/
+