wlan: Implement Private NL CMD handling for roaming

When supplicant issue enabled / disable firmware based
roaming on the basis of the Bssid modification in network
block for an (e.g. AutoJoin modify Network Block N/W),
That time middle-ware will issue this pvt cmd to Driver.
Driver need to disable / enable firmware roaming.
(in our current design we can enable firmware roaming
by offload scan to firmware so firmware can scan and
find candidate ap when rssi threshold meet the criteria,
same way driver can stop firmware roaming by stop offload
scan to firmware).

Change-Id: I00334b2190d20397518946018c4e5f3f644f3754
CRs-Fixed: 737976
diff --git a/CORE/HDD/src/wlan_hdd_cfg80211.c b/CORE/HDD/src/wlan_hdd_cfg80211.c
index bad6ebe..8d28350 100644
--- a/CORE/HDD/src/wlan_hdd_cfg80211.c
+++ b/CORE/HDD/src/wlan_hdd_cfg80211.c
@@ -4503,8 +4503,75 @@
     return 0;
 }
 
+const struct
+nla_policy qca_wlan_vendor_attr[QCA_WLAN_VENDOR_ATTR_MAX+1] =
+{
+    [QCA_WLAN_VENDOR_ATTR_ROAMING_POLICY] = { .type = NLA_U32 },
+    [QCA_WLAN_VENDOR_ATTR_MAC_ADDR]       = { .type = NLA_UNSPEC },
+};
+
+static int wlan_hdd_cfg80211_firmware_roaming(struct wiphy *wiphy,
+            struct wireless_dev *wdev, void *data, int data_len)
+{
+
+    u8 bssid[6]                                 = {0};
+    hdd_context_t *pHddCtx                      = wiphy_priv(wiphy);
+    struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_MAX + 1];
+    eHalStatus status = eHAL_STATUS_SUCCESS;
+    v_U32_t isFwrRoamEnabled = FALSE;
+    int ret;
+
+    if (NULL == pHddCtx) {
+        hddLog(VOS_TRACE_LEVEL_ERROR,
+               FL("HDD context is not valid"));
+        return -EINVAL;
+    }
+
+    ret = nla_parse(tb, QCA_WLAN_VENDOR_ATTR_MAX,
+                    data, data_len,
+                    qca_wlan_vendor_attr);
+    if (ret){
+        hddLog(VOS_TRACE_LEVEL_ERROR, FL("Invalid ATTR"));
+        return -EINVAL;
+    }
+
+    /* Parse and fetch Enable flag */
+    if (!tb[QCA_WLAN_VENDOR_ATTR_ROAMING_POLICY]) {
+        hddLog(VOS_TRACE_LEVEL_ERROR, FL("attr enable failed"));
+        return -EINVAL;
+    }
+
+    isFwrRoamEnabled = nla_get_u32(
+         tb[QCA_WLAN_VENDOR_ATTR_ROAMING_POLICY]);
+
+    hddLog(VOS_TRACE_LEVEL_INFO, FL("isFwrRoamEnabled (%d)"), isFwrRoamEnabled);
+
+    /* Parse and fetch bssid */
+    if (!tb[QCA_WLAN_VENDOR_ATTR_MAC_ADDR]) {
+        hddLog(VOS_TRACE_LEVEL_ERROR, FL("attr bss id failed"));
+        return -EINVAL;
+    }
+
+    memcpy(bssid, nla_data(
+                tb[QCA_WLAN_VENDOR_ATTR_MAC_ADDR]),
+                sizeof(bssid));
+    hddLog(VOS_TRACE_LEVEL_INFO, FL(MAC_ADDRESS_STR),MAC_ADDR_ARRAY(bssid));
+
+    //Update roaming
+    status = sme_ConfigFwrRoaming((tHalHandle)(pHddCtx->hHal), isFwrRoamEnabled);
+    return status;
+}
+
 const struct wiphy_vendor_command hdd_wiphy_vendor_commands[] =
 {
+    {
+        .info.vendor_id = QCA_NL80211_VENDOR_ID,
+        .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_ROAMING,
+        .flags = WIPHY_VENDOR_CMD_NEED_WDEV |
+                 WIPHY_VENDOR_CMD_NEED_NETDEV |
+                 WIPHY_VENDOR_CMD_NEED_RUNNING,
+        .doit = wlan_hdd_cfg80211_firmware_roaming
+    },
 #ifdef WLAN_FEATURE_LINK_LAYER_STATS
     {
         .info.vendor_id = QCA_NL80211_VENDOR_ID,