wlan: add support for vendor specific event

Add support for vendor specific event and use it to provide
interfering frequency information to userspace. Userspace should
stop P2P-GO, in case P2P-GO is operating on interfering frequency
and restart P2P-GO by avoiding interfering frequency range(s). User
space may decide to continue operation on interfering frequency,
but in such case, there might be impact on performance.

CRs-Fixed: 531882
Change-Id: Ife3650d6ccb3c55aea38b5d9ba1e0b8b6c7fbca3
diff --git a/CORE/HDD/inc/wlan_hdd_cfg80211.h b/CORE/HDD/inc/wlan_hdd_cfg80211.h
index 979a797..1d78e19 100644
--- a/CORE/HDD/inc/wlan_hdd_cfg80211.h
+++ b/CORE/HDD/inc/wlan_hdd_cfg80211.h
@@ -123,6 +123,33 @@
 }__attribute__((packed)) qcom_ie_age ;
 #endif
 
+/* Vendor id to be used in vendor specific command and events
+ * to user space. Use QCA OUI 00:13:74 to match with define in
+ * supplicant code.
+ */
+#define QCOM_NL80211_VENDOR_ID                0x001374
+
+/* Vendor speicific sub-command id and their index */
+#ifdef FEATURE_WLAN_CH_AVOID
+#define QCOM_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY         10
+#define QCOM_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY_INDEX   0
+#endif /* FEATURE_WLAN_CH_AVOID */
+
+#ifdef FEATURE_WLAN_CH_AVOID
+#define HDD_MAX_AVOID_FREQ_RANGES   4
+typedef struct sHddAvoidFreqRange
+{
+   u32 startFreq;
+   u32 endFreq;
+} tHddAvoidFreqRange;
+
+typedef struct sHddAvoidFreqList
+{
+   u32 avoidFreqRangeCount;
+   tHddAvoidFreqRange avoidFreqRange[HDD_MAX_AVOID_FREQ_RANGES];
+} tHddAvoidFreqList;
+#endif /* FEATURE_WLAN_CH_AVOID */
+
 struct cfg80211_bss* wlan_hdd_cfg80211_update_bss_db( hdd_adapter_t *pAdapter,
                                       tCsrRoamInfo *pRoamInfo
                                       );
@@ -195,4 +222,7 @@
 void hdd_select_cbmode( hdd_adapter_t *pAdapter,v_U8_t operationChannel);
 
 
+int wlan_hdd_send_avoid_freq_event(hdd_context_t *pHddCtx,
+                                   tHddAvoidFreqList *pAvoidFreqList);
+
 #endif
diff --git a/CORE/HDD/inc/wlan_hdd_main.h b/CORE/HDD/inc/wlan_hdd_main.h
index 6371351..9f6905a 100644
--- a/CORE/HDD/inc/wlan_hdd_main.h
+++ b/CORE/HDD/inc/wlan_hdd_main.h
@@ -75,6 +75,8 @@
 #ifdef FEATURE_WLAN_TDLS
 #include "wlan_hdd_tdls.h"
 #endif
+#include "wlan_hdd_cfg80211.h"
+
 /*--------------------------------------------------------------------------- 
   Preprocessor definitions and constants
   -------------------------------------------------------------------------*/
diff --git a/CORE/HDD/src/wlan_hdd_cfg80211.c b/CORE/HDD/src/wlan_hdd_cfg80211.c
index 131ab42..0666ba1 100644
--- a/CORE/HDD/src/wlan_hdd_cfg80211.c
+++ b/CORE/HDD/src/wlan_hdd_cfg80211.c
@@ -535,6 +535,65 @@
 };
 #endif /* WLAN_NL80211_TESTMODE */
 
+#ifdef FEATURE_WLAN_CH_AVOID
+/*
+ * FUNCTION: wlan_hdd_send_avoid_freq_event
+ * This is called when wlan driver needs to send vendor specific
+ * avoid frequency range event to userspace
+ */
+int wlan_hdd_send_avoid_freq_event(hdd_context_t *pHddCtx,
+                                   tHddAvoidFreqList *pAvoidFreqList)
+{
+    struct sk_buff *vendor_event;
+
+    ENTER();
+
+    if (!pHddCtx)
+    {
+        VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
+                  "%s: HDD context is null", __func__);
+        return -1;
+    }
+
+    if (!pAvoidFreqList)
+    {
+        VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
+                  "%s: pAvoidFreqList is null", __func__);
+        return -1;
+    }
+
+    vendor_event = cfg80211_vendor_event_alloc(pHddCtx->wiphy,
+                       sizeof(tHddAvoidFreqList),
+                       QCOM_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY_INDEX,
+                       GFP_KERNEL);
+    if (!vendor_event)
+    {
+        VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
+                  "%s: cfg80211_vendor_event_alloc failed", __func__);
+        return -1;
+    }
+
+    memcpy(skb_put(vendor_event, sizeof(tHddAvoidFreqList)),
+                   (void *)pAvoidFreqList, sizeof(tHddAvoidFreqList));
+
+    cfg80211_vendor_event(vendor_event, GFP_KERNEL);
+
+    EXIT();
+    return 0;
+}
+#endif /* FEATURE_WLAN_CH_AVOID */
+
+/* vendor specific events */
+static const struct nl80211_vendor_cmd_info wlan_hdd_cfg80211_vendor_events[] =
+{
+#ifdef FEATURE_WLAN_CH_AVOID
+    {
+        .vendor_id = QCOM_NL80211_VENDOR_ID,
+        .subcmd = QCOM_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY
+    },
+#endif /* FEATURE_WLAN_CH_AVOID */
+};
+
 /*
  * FUNCTION: wlan_hdd_cfg80211_wiphy_alloc
  * This function is called by hdd_wlan_startup()
@@ -792,6 +851,10 @@
     wiphy->max_remain_on_channel_duration = 1000;
 #endif
 
+    wiphy->n_vendor_commands = 0;
+    wiphy->vendor_events = wlan_hdd_cfg80211_vendor_events;
+    wiphy->n_vendor_events = ARRAY_SIZE(wlan_hdd_cfg80211_vendor_events);
+
     EXIT();
     return 0;
 }
diff --git a/CORE/HDD/src/wlan_hdd_hostapd.c b/CORE/HDD/src/wlan_hdd_hostapd.c
index a04f6e8..1786856 100644
--- a/CORE/HDD/src/wlan_hdd_hostapd.c
+++ b/CORE/HDD/src/wlan_hdd_hostapd.c
@@ -1216,6 +1216,8 @@
    v_U16_t             unsafeChannelCount = 0;
    v_U16_t             unsafeChannelList[NUM_20MHZ_RF_CHANNELS];
    v_CONTEXT_t         pVosContext;
+   tHddAvoidFreqList   hddAvoidFreqList;
+   tANI_U32            i;
 
    /* Basic sanity */
    if ((NULL == pAdapter) || (NULL == indParam))
@@ -1294,6 +1296,19 @@
                                           unsafeChannelList,
                                           unsafeChannelCount);
 
+   /* generate vendor specific event */
+   vos_mem_zero((void *)&hddAvoidFreqList, sizeof(tHddAvoidFreqList));
+   for (i = 0; i < chAvoidInd->avoidRangeCount; i++)
+   {
+      hddAvoidFreqList.avoidFreqRange[i].startFreq =
+            chAvoidInd->avoidFreqRange[i].startFreq;
+      hddAvoidFreqList.avoidFreqRange[i].endFreq =
+            chAvoidInd->avoidFreqRange[i].endFreq;
+   }
+   hddAvoidFreqList.avoidFreqRangeCount = chAvoidInd->avoidRangeCount;
+
+   wlan_hdd_send_avoid_freq_event(hddCtxt, &hddAvoidFreqList);
+
    /* Get SAP context first
     * SAP and P2PGO would not concurrent */
    pHostapdAdapter = hdd_get_adapter(hddCtxt, WLAN_HDD_SOFTAP);