prima: Support for get_concurrency_matrix

Provide the driver support to return concurrency matrix
where each index represents a combination of features which work
concurrently.

Change-Id: Ia929b139f4cf61bc546af23a9b6ee54944e55ea9
CRs-Fixed: 746842
diff --git a/CORE/HDD/src/wlan_hdd_cfg80211.c b/CORE/HDD/src/wlan_hdd_cfg80211.c
index 8d28350..f6f5a19 100644
--- a/CORE/HDD/src/wlan_hdd_cfg80211.c
+++ b/CORE/HDD/src/wlan_hdd_cfg80211.c
@@ -143,6 +143,7 @@
 #endif
 
 #define HDD_CHANNEL_14 14
+#define WLAN_HDD_MAX_FEATURE_SET   8
 
 #ifdef WLAN_FEATURE_LINK_LAYER_STATS
 /*
@@ -4452,6 +4453,87 @@
     return -EINVAL;
 }
 
+static int
+wlan_hdd_cfg80211_get_concurrency_matrix(struct wiphy *wiphy,
+                                         struct wireless_dev *wdev,
+                                         void *data, int data_len)
+{
+    uint32_t feature_set_matrix[WLAN_HDD_MAX_FEATURE_SET] = {0};
+    uint8_t i, feature_sets, max_feature_sets;
+    struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_GET_CONCURRENCY_MATRIX_MAX + 1];
+    struct sk_buff *reply_skb;
+
+    ENTER();
+
+    if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_GET_CONCURRENCY_MATRIX_MAX,
+                  data, data_len, NULL)) {
+        hddLog(LOGE, FL("Invalid ATTR"));
+        return -EINVAL;
+    }
+
+    /* Parse and fetch max feature set */
+    if (!tb[QCA_WLAN_VENDOR_ATTR_GET_CONCURRENCY_MATRIX_CONFIG_PARAM_SET_SIZE_MAX]) {
+        hddLog(LOGE, FL("Attr max feature set size failed"));
+        return -EINVAL;
+    }
+    max_feature_sets = nla_get_u32(
+     tb[QCA_WLAN_VENDOR_ATTR_GET_CONCURRENCY_MATRIX_CONFIG_PARAM_SET_SIZE_MAX]);
+    hddLog(LOG1, FL("Max feature set size (%d)"), max_feature_sets);
+
+    /* Fill feature combination matrix */
+    feature_sets = 0;
+    if (feature_sets >= WLAN_HDD_MAX_FEATURE_SET) goto max_buffer_err;
+    feature_set_matrix[feature_sets++] = WIFI_FEATURE_INFRA |
+                                         WIFI_FEATURE_P2P;
+
+    if (feature_sets >= WLAN_HDD_MAX_FEATURE_SET) goto max_buffer_err;
+    feature_set_matrix[feature_sets++] = WIFI_FEATURE_INFRA |
+                                         WIFI_FEATURE_SOFT_AP;
+
+    if (feature_sets >= WLAN_HDD_MAX_FEATURE_SET) goto max_buffer_err;
+    feature_set_matrix[feature_sets++] = WIFI_FEATURE_P2P |
+                                         WIFI_FEATURE_SOFT_AP;
+
+    if (feature_sets >= WLAN_HDD_MAX_FEATURE_SET) goto max_buffer_err;
+    feature_set_matrix[feature_sets++] = WIFI_FEATURE_INFRA |
+                                         WIFI_FEATURE_SOFT_AP |
+                                         WIFI_FEATURE_P2P;
+
+    /* Add more feature combinations here */
+
+    feature_sets = VOS_MIN(feature_sets, max_feature_sets);
+    hddLog(LOG1, FL("Number of feature sets (%d)"), feature_sets);
+    hddLog(LOG1, "Feature set matrix");
+    for (i = 0; i < feature_sets; i++)
+        hddLog(LOG1, "[%d] 0x%02X", i, feature_set_matrix[i]);
+
+    reply_skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, sizeof(u32) +
+                                                    sizeof(u32) * feature_sets +
+                                                    NLMSG_HDRLEN);
+
+    if (reply_skb) {
+        if (nla_put_u32(reply_skb,
+                        QCA_WLAN_VENDOR_ATTR_GET_CONCURRENCY_MATRIX_RESULTS_SET_SIZE,
+                        feature_sets) ||
+            nla_put(reply_skb,
+                    QCA_WLAN_VENDOR_ATTR_GET_CONCURRENCY_MATRIX_RESULTS_SET,
+                    sizeof(u32) * feature_sets, feature_set_matrix)) {
+            hddLog(LOGE, FL("nla put fail"));
+            kfree_skb(reply_skb);
+            return -EINVAL;
+        }
+
+        return cfg80211_vendor_cmd_reply(reply_skb);
+    }
+    hddLog(LOGE, FL("Feature set matrix: buffer alloc fail"));
+    return -ENOMEM;
+
+max_buffer_err:
+    hddLog(LOGE, FL("Feature set max buffer size reached. feature_sets(%d) max(%d)"),
+           feature_sets, WLAN_HDD_MAX_FEATURE_SET);
+    return -EINVAL;
+}
+
 static const struct nla_policy
 wlan_hdd_set_no_dfs_flag_config_policy[QCA_WLAN_VENDOR_ATTR_SET_NO_DFS_FLAG_MAX
  +1] =
@@ -4718,6 +4800,13 @@
                  WIPHY_VENDOR_CMD_NEED_NETDEV,
         .doit = wlan_hdd_cfg80211_set_spoofed_mac_oui
     },
+    {
+        .info.vendor_id = QCA_NL80211_VENDOR_ID,
+        .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_GET_CONCURRENCY_MATRIX,
+        .flags = WIPHY_VENDOR_CMD_NEED_WDEV |
+                 WIPHY_VENDOR_CMD_NEED_NETDEV,
+        .doit = wlan_hdd_cfg80211_get_concurrency_matrix
+    },
 };
 
 /* vendor specific events */