DO NOT MERGE - Merge qt-dev-plus-aosp-without-vendor (5699924) into stage-aosp-master
am: 6e67c53763

Change-Id: I6ec117bcd920a42b09237409c4d50feb18b0c55f
diff --git a/qcwcn/wifi_hal/Android.mk b/qcwcn/wifi_hal/Android.mk
index 857f156..0b76f66 100644
--- a/qcwcn/wifi_hal/Android.mk
+++ b/qcwcn/wifi_hal/Android.mk
@@ -14,6 +14,25 @@
 
 LOCAL_PATH := $(call my-dir)
 
+# Control APIs used by clients to communicate with HAL.
+# ============================================================
+include $(CLEAR_VARS)
+
+LOCAL_CFLAGS := -Wno-unused-parameter
+LOCAL_CFLAGS += -Wall -Werror
+LOCAL_MODULE := libwifi-hal-ctrl
+LOCAL_VENDOR_MODULE := true
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/wifi_hal_ctrl
+LOCAL_SRC_FILES := wifi_hal_ctrl/wifi_hal_ctrl.c
+LOCAL_HEADER_LIBRARIES := libcutils_headers
+include $(BUILD_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libwifi-hal-ctrl_headers
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/wifi_hal_ctrl
+LOCAL_HEADER_LIBRARIES := libcutils_headers
+include $(BUILD_HEADER_LIBRARY)
+
 # Make the HAL library
 # ============================================================
 include $(CLEAR_VARS)
@@ -41,6 +60,7 @@
 	$(TARGET_OUT_HEADERS)/cld80211-lib
 
 LOCAL_SRC_FILES := \
+	list.cpp \
 	wifi_hal.cpp \
 	common.cpp \
 	cpp_bindings.cpp \
@@ -76,7 +96,7 @@
 LOCAL_C_INCLUDES += external/libnl-headers
 endif
 
-LOCAL_HEADER_LIBRARIES := libcutils_headers libutils_headers
+LOCAL_HEADER_LIBRARIES := libcutils_headers libutils_headers libwifi-hal-ctrl_headers
 
 include $(BUILD_STATIC_LIBRARY)
 
@@ -106,6 +126,7 @@
 	$(TARGET_OUT_HEADERS)/cld80211-lib
 
 LOCAL_SRC_FILES := \
+	list.cpp \
 	wifi_hal.cpp \
 	common.cpp \
 	cpp_bindings.cpp \
@@ -134,6 +155,7 @@
 LOCAL_CLANG := true
 LOCAL_SHARED_LIBRARIES += libnetutils liblog
 LOCAL_SHARED_LIBRARIES += libdl libwpa_client libcld80211
+LOCAL_SHARED_LIBRARIES += libwifi-hal-ctrl
 
 ifneq ($(wildcard external/libnl),)
 LOCAL_SHARED_LIBRARIES += libnl
@@ -143,5 +165,5 @@
 LOCAL_C_INCLUDES += external/libnl-headers
 endif
 
-LOCAL_HEADER_LIBRARIES := libcutils_headers libutils_headers
+LOCAL_HEADER_LIBRARIES := libcutils_headers libutils_headers libwifi-hal-ctrl_headers
 include $(BUILD_SHARED_LIBRARY)
diff --git a/qcwcn/wifi_hal/common.cpp b/qcwcn/wifi_hal/common.cpp
index c54577c..3d72e76 100644
--- a/qcwcn/wifi_hal/common.cpp
+++ b/qcwcn/wifi_hal/common.cpp
@@ -420,6 +420,8 @@
             return WIFI_ERROR_OUT_OF_MEMORY;
         case -EBUSY:
             return WIFI_ERROR_BUSY;
+        case -ENOBUFS:
+            return WIFI_ERROR_TOO_MANY_REQUESTS;
     }
     return WIFI_ERROR_UNKNOWN;
 }
diff --git a/qcwcn/wifi_hal/common.h b/qcwcn/wifi_hal/common.h
index 759ae01..0ed5a81 100644
--- a/qcwcn/wifi_hal/common.h
+++ b/qcwcn/wifi_hal/common.h
@@ -27,6 +27,7 @@
 #include <fcntl.h>
 #include <inttypes.h>
 #include <sys/socket.h>
+#include <sys/un.h>
 #include <netlink/genl/genl.h>
 #include <netlink/genl/family.h>
 #include <netlink/genl/ctrl.h>
@@ -53,6 +54,8 @@
 #define DEFAULT_EVENT_CB_SIZE   (64)
 #define NUM_RING_BUFS           5
 
+#define WIFI_HAL_CTRL_IFACE     "/dev/socket/wifihal/wifihal_ctrlsock"
+
 #define MAC_ADDR_ARRAY(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5]
 #define MAC_ADDR_STR "%02x:%02x:%02x:%02x:%02x:%02x"
 #define BIT(x) (1 << (x))
@@ -105,11 +108,18 @@
 struct rssi_monitor_event_handler_s;
 struct cld80211_ctx;
 
+struct ctrl_sock {
+    int s;
+    struct sockaddr_un local;
+};
+
 typedef struct hal_info_s {
 
     struct nl_sock *cmd_sock;                       // command socket object
     struct nl_sock *event_sock;                     // event socket object
     struct nl_sock *user_sock;                      // user socket object
+    struct ctrl_sock wifihal_ctrl_sock;             // ctrl sock object
+    struct list_head monitor_sockets;               // list of monitor sockets
     int nl80211_family_id;                          // family id for 80211 driver
 
     bool in_event_loop;                             // Indicates that event loop is active
diff --git a/qcwcn/wifi_hal/list.cpp b/qcwcn/wifi_hal/list.cpp
new file mode 100644
index 0000000..966da2e
--- /dev/null
+++ b/qcwcn/wifi_hal/list.cpp
@@ -0,0 +1,76 @@
+/* Copyright (c) 2019 The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of The Linux Foundation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdlib.h>
+#include "list.h"
+
+void INITIALISE_LIST(struct list_head *list)
+{
+  list->next = list;
+  list->prev = list;
+}
+
+void list_add(struct list_head *latest,
+                struct list_head *prev, struct list_head *next)
+{
+  next->prev = latest;
+  latest->next = next;
+  latest->prev = prev;
+  prev->next = latest;
+}
+
+void add_to_list(struct list_head *latest, struct list_head *head)
+{
+  list_add(latest, head, head->next);
+}
+
+void list_add_tail(struct list_head *latest, struct list_head *head)
+{
+  list_add(latest, head->prev, head);
+}
+
+void list_del(struct list_head *prev, struct list_head *next)
+{
+  next->prev = prev;
+  prev->next = next;
+}
+
+void del_from_list(struct list_head *record)
+{
+  list_del(record->prev, record->next);
+  record->next = NULL;
+  record->prev = NULL;
+}
+
+void replace_in_list(struct list_head *old, struct list_head *latest)
+{
+  latest->next = old->next;
+  latest->next->prev = latest;
+  latest->prev = old->prev;
+  latest->prev->next = latest;
+}
diff --git a/qcwcn/wifi_hal/list.h b/qcwcn/wifi_hal/list.h
new file mode 100644
index 0000000..90d344c
--- /dev/null
+++ b/qcwcn/wifi_hal/list.h
@@ -0,0 +1,72 @@
+/* Copyright (c) 2019 The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of The Linux Foundation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _WIFIHAL_LIST_H_
+#define _WIFIHAL_LIST_H_
+
+struct list_head {
+  struct list_head *next, *prev;
+};
+
+void INITIALISE_LIST(struct list_head *list);
+void list_add(struct list_head *latest, struct list_head *prev,
+                  struct list_head *next);
+void add_to_list(struct list_head *latest, struct list_head *head);
+void list_add_tail(struct list_head *latest, struct list_head *head);
+void list_del(struct list_head *prev, struct list_head *next);
+void del_from_list(struct list_head *record);
+void replace_in_list(struct list_head *old, struct list_head *latest);
+
+#define list_for_each(ref, head) \
+        for (ref = (head)->next; ref->next, ref != (head); ref = ref->next)
+
+#define offset_of(type, member) ((char *)&((type *)0)->member)
+
+#define container_of(ptr, type, member) ({ \
+        const typeof(((type *)0)->member) *__mptr = (ptr); \
+        (type *)((char *)__mptr - offset_of(type, member)); })
+
+#define list_entry(ptr, type, member) \
+        container_of(ptr, type, member)
+
+#define list_for_each_entry(ref, head, member) \
+        for (ref = list_entry((head)->next, typeof(*ref), member); \
+             ref->member.next, &ref->member != (head); \
+             ref = list_entry(ref->member.next, typeof(*ref), member))
+
+#define list_for_each_entry_safe(pos, n, head, member) \
+        for (pos = list_entry((head)->next, typeof(*pos), member), \
+             n = list_entry(pos->member.next, typeof(*pos), member); \
+             &pos->member != (head); \
+             pos = n, n = list_entry(n->member.next, typeof(*n), member))
+
+#define list_for_each_safe(pos, n, head) \
+        for (pos = (head)->next, n = pos->next; pos != (head); \
+             pos = n, n = pos->next)
+
+#endif
diff --git a/qcwcn/wifi_hal/wifi_hal.cpp b/qcwcn/wifi_hal/wifi_hal.cpp
index 198ab01..cb82885 100644
--- a/qcwcn/wifi_hal/wifi_hal.cpp
+++ b/qcwcn/wifi_hal/wifi_hal.cpp
@@ -41,6 +41,7 @@
 #include <cld80211_lib.h>
 
 #include <sys/types.h>
+#include "list.h"
 #include <unistd.h>
 
 #include "sync.h"
@@ -48,6 +49,7 @@
 #define LOG_TAG  "WifiHAL"
 
 #include "wifi_hal.h"
+#include "wifi_hal_ctrl.h"
 #include "common.h"
 #include "cpp_bindings.h"
 #include "ifaceeventhandler.h"
@@ -73,6 +75,19 @@
 #define POLL_DRIVER_DURATION_US (100000)
 #define POLL_DRIVER_MAX_TIME_MS (10000)
 
+static int attach_monitor_sock(wifi_handle handle, wifihal_ctrl_req_t *ctrl_msg);
+
+static int dettach_monitor_sock(wifi_handle handle, wifihal_ctrl_req_t *ctrl_msg);
+
+static int register_monitor_sock(wifi_handle handle, wifihal_ctrl_req_t *ctrl_msg, int attach);
+
+static int send_nl_data(wifi_handle handle, wifihal_ctrl_req_t *ctrl_msg);
+
+static int internal_pollin_handler(wifi_handle handle, struct nl_sock *sock);
+
+static void internal_event_handler_app(wifi_handle handle, int events,
+                                       struct ctrl_sock *sock);
+
 static void internal_event_handler(wifi_handle handle, int events,
                                    struct nl_sock *sock);
 static int internal_valid_message_handler(nl_msg *msg, void *arg);
@@ -135,6 +150,105 @@
     return sock;
 }
 
+void wifi_create_ctrl_socket(hal_info *info)
+{
+#ifdef ANDROID
+   struct group *grp_wifi;
+   gid_t gid_wifi;
+   struct passwd *pwd_system;
+   uid_t uid_system;
+#endif
+
+    int flags;
+
+    info->wifihal_ctrl_sock.s = socket(PF_UNIX, SOCK_DGRAM, 0);
+
+    if (info->wifihal_ctrl_sock.s < 0) {
+        ALOGE("socket(PF_UNIX): %s", strerror(errno));
+        return;
+    }
+    memset(&info->wifihal_ctrl_sock.local, 0, sizeof(info->wifihal_ctrl_sock.local));
+
+    info->wifihal_ctrl_sock.local.sun_family = AF_UNIX;
+
+    snprintf(info->wifihal_ctrl_sock.local.sun_path,
+             sizeof(info->wifihal_ctrl_sock.local.sun_path), "%s", WIFI_HAL_CTRL_IFACE);
+
+    if (bind(info->wifihal_ctrl_sock.s, (struct sockaddr *) &info->wifihal_ctrl_sock.local,
+             sizeof(info->wifihal_ctrl_sock.local)) < 0) {
+        ALOGD("ctrl_iface bind(PF_UNIX) failed: %s",
+               strerror(errno));
+        if (connect(info->wifihal_ctrl_sock.s, (struct sockaddr *) &info->wifihal_ctrl_sock.local,
+                    sizeof(info->wifihal_ctrl_sock.local)) < 0) {
+                ALOGD("ctrl_iface exists, but does not"
+                      " allow connections - assuming it was left"
+                      "over from forced program termination");
+                if (unlink(info->wifihal_ctrl_sock.local.sun_path) < 0) {
+                   ALOGE("Could not unlink existing ctrl_iface socket '%s': %s",
+                          info->wifihal_ctrl_sock.local.sun_path, strerror(errno));
+                   goto out;
+
+                }
+                if (bind(info->wifihal_ctrl_sock.s ,
+                         (struct sockaddr *) &info->wifihal_ctrl_sock.local,
+                         sizeof(info->wifihal_ctrl_sock.local)) < 0) {
+                        ALOGE("wifihal-ctrl-iface-init: bind(PF_UNIX): %s",
+                               strerror(errno));
+                        goto out;
+                }
+                ALOGD("Successfully replaced leftover "
+                      "ctrl_iface socket '%s'", info->wifihal_ctrl_sock.local.sun_path);
+        } else {
+             ALOGI("ctrl_iface exists and seems to "
+                   "be in use - cannot override it");
+             ALOGI("Delete '%s' manually if it is "
+                   "not used anymore", info->wifihal_ctrl_sock.local.sun_path);
+             goto out;
+        }
+    }
+
+    /*
+     * Make socket non-blocking so that we don't hang forever if
+     * target dies unexpectedly.
+     */
+
+#ifdef ANDROID
+    if (chmod(info->wifihal_ctrl_sock.local.sun_path, S_IRWXU | S_IRWXG) < 0)
+    {
+      ALOGE("Failed to give permissions: %s", strerror(errno));
+    }
+
+    /* Set group even if we do not have privileges to change owner */
+    grp_wifi = getgrnam("wifi");
+    gid_wifi = grp_wifi ? grp_wifi->gr_gid : 0;
+    pwd_system = getpwnam("system");
+    uid_system = pwd_system ? pwd_system->pw_uid : 0;
+    if (!gid_wifi || !uid_system) {
+      ALOGE("Failed to get grp ids");
+      unlink(info->wifihal_ctrl_sock.local.sun_path);
+      goto out;
+    }
+    chown(info->wifihal_ctrl_sock.local.sun_path, -1, gid_wifi);
+    chown(info->wifihal_ctrl_sock.local.sun_path, uid_system, gid_wifi);
+#endif
+
+    flags = fcntl(info->wifihal_ctrl_sock.s, F_GETFL);
+    if (flags >= 0) {
+        flags |= O_NONBLOCK;
+        if (fcntl(info->wifihal_ctrl_sock.s, F_SETFL, flags) < 0) {
+            ALOGI("fcntl(ctrl, O_NONBLOCK): %s",
+                   strerror(errno));
+            /* Not fatal, continue on.*/
+        }
+    }
+  return;
+
+out:
+  close(info->wifihal_ctrl_sock.s);
+  info->wifihal_ctrl_sock.s = 0;
+  return;
+}
+
 int ack_handler(struct nl_msg *msg, void *arg)
 {
     int *err = (int *)arg;
@@ -471,6 +585,7 @@
     cld80211_remove_mcast_group(info->cldctx, "per_pkt_stats");
     cld80211_remove_mcast_group(info->cldctx, "diag_events");
     cld80211_remove_mcast_group(info->cldctx, "fatal_events");
+    cld80211_remove_mcast_group(info->cldctx, "oem_msgs");
     exit_cld80211_recv(info->cldctx);
     cld80211_deinit(info->cldctx);
     info->cldctx = NULL;
@@ -584,6 +699,13 @@
     wifi_add_membership(*handle, "regulatory");
     wifi_add_membership(*handle, "vendor");
 
+    info->wifihal_ctrl_sock.s = 0;
+
+    wifi_create_ctrl_socket(info);
+
+    //! Initailise the monitoring clients list
+    INITIALISE_LIST(&info->monitor_sockets);
+
     info->cldctx = cld80211_init();
     if (info->cldctx != NULL) {
         info->user_sock = info->cldctx->sock;
@@ -618,6 +740,15 @@
             ALOGE("Failed to add mcast group fatal_events :%d", status);
             goto cld80211_cleanup;
         }
+
+        if(info->wifihal_ctrl_sock.s > 0)
+        {
+          status = cld80211_add_mcast_group(info->cldctx, "oem_msgs");
+          if (status) {
+             ALOGE("Failed to add mcast group oem_msgs :%d", status);
+             goto cld80211_cleanup;
+          }
+        }
     } else {
         ret = wifi_init_user_sock(info);
         if (ret != WIFI_SUCCESS) {
@@ -687,7 +818,7 @@
         }
         ALOGV("%s: hardware version type %d", __func__, info->pkt_log_ver);
     } else {
-        ALOGE("Failed to get supported logger feature set: %d", ret);
+        ALOGE("Failed to get firmware version: %d", ret);
     }
 
     ret = get_firmware_bus_max_size_supported(iface_handle);
@@ -826,6 +957,7 @@
 {
     hal_info *info = getHalInfo(handle);
     wifi_cleaned_up_handler cleaned_up_handler = info->cleaned_up_handler;
+    wifihal_mon_sock_t *reg, *tmp;
 
     if (info->cmd_sock != 0) {
         nl_socket_free(info->cmd_sock);
@@ -834,6 +966,19 @@
         info->event_sock = NULL;
     }
 
+    if (info->wifihal_ctrl_sock.s != 0) {
+        close(info->wifihal_ctrl_sock.s);
+        unlink(info->wifihal_ctrl_sock.local.sun_path);
+        info->wifihal_ctrl_sock.s = 0;
+    }
+
+   list_for_each_entry_safe(reg, tmp, &info->monitor_sockets, list) {
+        del_from_list(&reg->list);
+        if(reg) {
+           free(reg);
+        }
+    }
+
     if (info->interfaces) {
         for (int i = 0; i < info->num_interfaces; i++)
             free(info->interfaces[i]);
@@ -901,6 +1046,352 @@
     ALOGI("Sent msg on exit sock to unblock poll()");
 }
 
+
+
+static int validate_cld80211_msg(nlmsghdr *nlh, int family, int cmd)
+{
+    //! Enhance this API
+    struct genlmsghdr *hdr;
+    hdr = (genlmsghdr *)nlmsg_data(nlh);
+
+    if(hdr->cmd == WLAN_NL_MSG_OEM)
+    {
+      ALOGV("%s: FAMILY ID : %d ,NL CMD : %d received", __FUNCTION__,
+             nlh->nlmsg_type, hdr->cmd);
+
+      //! Update pid with the wifihal pid
+      nlh->nlmsg_pid = getpid();
+      return 0;
+    }
+    else
+    {
+      ALOGE("%s: NL CMD : %d received is not allowed", __FUNCTION__, hdr->cmd);
+      return -1;
+    }
+}
+
+
+static int validate_genl_msg(nlmsghdr *nlh, int family, int cmd)
+{
+    //! Enhance this API
+    struct genlmsghdr *hdr;
+    hdr = (genlmsghdr *)nlmsg_data(nlh);
+
+    if(hdr->cmd == NL80211_CMD_FRAME ||
+       hdr->cmd == NL80211_CMD_REGISTER_ACTION)
+    {
+      ALOGV("%s: FAMILY ID : %d ,NL CMD : %d received", __FUNCTION__,
+             nlh->nlmsg_type, hdr->cmd);
+      return 0;
+    }
+    else
+    {
+      ALOGE("%s: NL CMD : %d received is not allowed", __FUNCTION__, hdr->cmd);
+      return -1;
+    }
+}
+
+static int send_nl_data(wifi_handle handle, wifihal_ctrl_req_t *ctrl_msg)
+{
+    hal_info *info = getHalInfo(handle);
+    struct nl_msg *msg = NULL;
+    int retval = -1;
+
+    //! attach monitor socket if it was not it the list
+    if(ctrl_msg->monsock_len)
+    {
+      retval = attach_monitor_sock(handle, ctrl_msg);
+      if(retval)
+        goto nl_out;
+    }
+
+    msg = nlmsg_alloc();
+    if (!msg)
+    {
+       ALOGE("%s: Memory allocation failed \n", __FUNCTION__);
+       goto nl_out;
+    }
+
+    memcpy((char *)msg->nm_nlh, (char *)ctrl_msg->data, ctrl_msg->data_len);
+
+   if(ctrl_msg->family_name == GENERIC_NL_FAMILY)
+   {
+     //! Before sending the received gennlmsg to kernel,
+     //! better to have checks for allowed commands
+     retval = validate_genl_msg(msg->nm_nlh, ctrl_msg->family_name, ctrl_msg->cmd_id);
+     if (retval < 0)
+         goto nl_out;
+
+     retval = nl_send_auto_complete(info->event_sock, msg);    /* send message */
+     if (retval < 0)
+     {
+       ALOGE("%s: nl_send_auto_complete - failed : %d \n", __FUNCTION__, retval);
+       goto nl_out;
+     }
+
+     retval = internal_pollin_handler(handle, info->event_sock);
+  }
+  else if (ctrl_msg->family_name == CLD80211_FAMILY)
+  {
+    if (info->cldctx != NULL)
+    {
+      //! Before sending the received cld80211 msg to kernel,
+      //! better to have checks for allowed commands
+      retval = validate_cld80211_msg(msg->nm_nlh, ctrl_msg->family_name, ctrl_msg->cmd_id);
+      if (retval < 0)
+         goto nl_out;
+
+      retval = cld80211_send_msg(info->cldctx, msg);
+      if (retval != 0)
+      {
+        ALOGE("%s: send cld80211 message - failed\n", __FUNCTION__);
+        goto nl_out;
+      }
+      ALOGD("%s: sent cld80211 message for pid %d\n", __FUNCTION__, getpid());
+    }
+    else
+    {
+      ALOGE("%s: cld80211 ctx not present \n", __FUNCTION__);
+    }
+  }
+  else
+  {
+    ALOGE("%s: Unknown family name : %d \n", __FUNCTION__, ctrl_msg->family_name);
+    retval = -1;
+  }
+nl_out:
+  if (msg)
+  {
+    nlmsg_free(msg);
+  }
+  return retval;
+}
+
+static int register_monitor_sock(wifi_handle handle, wifihal_ctrl_req_t *ctrl_msg, int attach)
+{
+    hal_info *info = getHalInfo(handle);
+
+    wifihal_mon_sock_t *reg, *nreg;
+    char *match = NULL;
+    unsigned int match_len = 0;
+    unsigned int type;
+
+    //! For Register Action frames, compare the match length and match buffer.
+    //! For other registrations such as oem messages,
+    //! diag messages check for respective commands
+
+    if((ctrl_msg->family_name == GENERIC_NL_FAMILY) &&
+       (ctrl_msg->cmd_id == NL80211_CMD_REGISTER_ACTION))
+    {
+       struct genlmsghdr *genlh;
+       struct  nlmsghdr *nlh = (struct  nlmsghdr *)ctrl_msg->data;
+       genlh = (struct genlmsghdr *)nlmsg_data(nlh);
+       struct nlattr *nlattrs[NL80211_ATTR_MAX + 1];
+
+       nla_parse(nlattrs, NL80211_ATTR_MAX, genlmsg_attrdata(genlh, 0),
+                 genlmsg_attrlen(genlh, 0), NULL);
+
+       if (!nlattrs[NL80211_ATTR_FRAME_TYPE])
+       {
+         ALOGD("No Valid frame type");
+       }
+       else
+       {
+         type = nla_get_u16(nlattrs[NL80211_ATTR_FRAME_TYPE]);
+       }
+       if (!nlattrs[NL80211_ATTR_FRAME_MATCH])
+       {
+         ALOGE("No Frame Match");
+         return -1;
+       }
+       else
+       {
+         match_len = nla_len(nlattrs[NL80211_ATTR_FRAME_MATCH]);
+         match = (char *)nla_data(nlattrs[NL80211_ATTR_FRAME_MATCH]);
+
+         list_for_each_entry(reg, &info->monitor_sockets, list) {
+
+           if(reg == NULL)
+              break;
+
+           int mlen = min(match_len, reg->match_len);
+
+           if (reg->match_len == 0)
+               continue;
+
+           if (memcmp(reg->match, match, mlen) == 0) {
+
+              if((ctrl_msg->monsock_len == reg->monsock_len) &&
+                 (memcmp((char *)&reg->monsock, (char *)&ctrl_msg->monsock, ctrl_msg->monsock_len) == 0))
+              {
+                if(attach)
+                {
+                  ALOGE(" %s :Action frame already registered for this client ", __FUNCTION__);
+                  return -2;
+                }
+                else
+                {
+                  del_from_list(&reg->list);
+                  free(reg);
+                  return 0;
+                }
+              }
+              else
+              {
+                //! when action frame registered for other client,
+                //! you can't attach or dettach for new client
+                ALOGE(" %s :Action frame registered for other client ", __FUNCTION__);
+                return -2;
+              }
+           }
+         }
+       }
+    }
+    else
+    {
+      list_for_each_entry(reg, &info->monitor_sockets, list) {
+
+         //! Checking for monitor sock in the list :
+
+         //! For attach request :
+         //! if sock is not present, then it is a new entry , so add to list.
+         //! if sock is present,  and cmd_id does not match, add another entry to list.
+         //! if sock is present, and cmd_id matches, return 0.
+
+         //! For dettach req :
+         //! if sock is not present, return error -2.
+         //! if sock is present,  and cmd_id does not match, return error -2.
+         //! if sock is present, and cmd_id matches, delete entry and return 0.
+         if(reg == NULL)
+            break;
+
+         if (ctrl_msg->monsock_len != reg->monsock_len)
+             continue;
+
+         if (memcmp((char *)&reg->monsock, (char *)&ctrl_msg->monsock, ctrl_msg->monsock_len) == 0) {
+
+            if((reg->family_name == ctrl_msg->family_name) && (reg->cmd_id == ctrl_msg->cmd_id))
+            {
+               if(!attach)
+               {
+                 del_from_list(&reg->list);
+                 free(reg);
+               }
+               return 0;
+            }
+         }
+      }
+    }
+
+    if(attach)
+    {
+       nreg = (wifihal_mon_sock_t *)malloc(sizeof(*reg) + match_len);
+        if (!nreg)
+           return -1;
+
+       memset((char *)nreg, 0, sizeof(*reg) + match_len);
+       nreg->family_name = ctrl_msg->family_name;
+       nreg->cmd_id = ctrl_msg->cmd_id;
+       nreg->monsock_len = ctrl_msg->monsock_len;
+       memcpy((char *)&nreg->monsock, (char *)&ctrl_msg->monsock, ctrl_msg->monsock_len);
+
+       if(match_len && match)
+       {
+         nreg->match_len = match_len;
+         memcpy(nreg->match, match, match_len);
+       }
+       add_to_list(&nreg->list, &info->monitor_sockets);
+    }
+    else
+    {
+       //! Not attached, so cant be dettached
+       ALOGE("%s: Dettaching the unregistered socket \n", __FUNCTION__);
+       return -2;
+    }
+
+   return 0;
+}
+
+static int attach_monitor_sock(wifi_handle handle, wifihal_ctrl_req_t *ctrl_msg)
+{
+   return register_monitor_sock(handle, ctrl_msg, 1);
+}
+
+static int dettach_monitor_sock(wifi_handle handle, wifihal_ctrl_req_t *ctrl_msg)
+{
+   return register_monitor_sock(handle, ctrl_msg, 0);
+}
+
+static int internal_pollin_handler_app(wifi_handle handle,  struct ctrl_sock *sock)
+{
+    int retval = -1;
+    int res;
+    struct sockaddr_un from;
+    socklen_t fromlen = sizeof(from);
+    wifihal_ctrl_req_t *ctrl_msg;
+    wifihal_ctrl_sync_rsp_t ctrl_reply;
+
+    ctrl_msg = (wifihal_ctrl_req_t *)malloc(DEFAULT_PAGE_SIZE);
+    if(ctrl_msg == NULL)
+    {
+      ALOGE ("Memory allocation failure");
+      return -1;
+    }
+
+    memset((char *)ctrl_msg, 0, DEFAULT_PAGE_SIZE);
+
+    res = recvfrom(sock->s, (char *)ctrl_msg, DEFAULT_PAGE_SIZE, 0,
+                   (struct sockaddr *)&from, &fromlen);
+    if (res < 0) {
+        ALOGE("recvfrom(ctrl_iface): %s",
+               strerror(errno));
+        if(ctrl_msg)
+           free(ctrl_msg);
+
+        return 0;
+    }
+    switch(ctrl_msg->ctrl_cmd)
+    {
+       case WIFIHAL_CTRL_MONITOR_ATTACH:
+         retval = attach_monitor_sock(handle, ctrl_msg);
+       break;
+       case WIFIHAL_CTRL_MONITOR_DETTACH:
+         retval = dettach_monitor_sock(handle, ctrl_msg);
+       break;
+       case WIFIHAL_CTRL_SEND_NL_DATA:
+         retval = send_nl_data(handle, ctrl_msg);
+       break;
+       default:
+       break;
+    }
+
+    ctrl_reply.ctrl_cmd = ctrl_msg->ctrl_cmd;
+    ctrl_reply.family_name = ctrl_msg->family_name;
+    ctrl_reply.cmd_id = ctrl_msg->cmd_id;
+    ctrl_reply.status = retval;
+
+    if(ctrl_msg)
+       free(ctrl_msg);
+
+    if (sendto(sock->s, (char *)&ctrl_reply, sizeof(ctrl_reply), 0, (struct sockaddr *)&from,
+               fromlen) < 0) {
+                  int _errno = errno;
+                  ALOGE("socket send failed : %d",_errno);
+
+       if (_errno == ENOBUFS || _errno == EAGAIN) {
+           /*
+            * The socket send buffer could be full. This
+            * may happen if client programs are not
+            * receiving their pending messages. Close and
+            * reopen the socket as a workaround to avoid
+            * getting stuck being unable to send any new
+            * responses.
+            */
+          }
+        }
+      return res;
+}
+
 static int internal_pollin_handler(wifi_handle handle, struct nl_sock *sock)
 {
     struct nl_cb *cb = nl_socket_get_cb(sock);
@@ -912,6 +1403,22 @@
     return res;
 }
 
+static void internal_event_handler_app(wifi_handle handle, int events,
+                                    struct ctrl_sock *sock)
+{
+    if (events & POLLERR) {
+        ALOGE("Error reading from wifi_hal ctrl socket");
+        internal_pollin_handler_app(handle, sock);
+    } else if (events & POLLHUP) {
+        ALOGE("Remote side hung up");
+    } else if (events & POLLIN) {
+        //ALOGI("Found some events!!!");
+        internal_pollin_handler_app(handle, sock);
+    } else {
+        ALOGE("Unknown event - %0x", events);
+    }
+}
+
 static void internal_event_handler(wifi_handle handle, int events,
                                    struct nl_sock *sock)
 {
@@ -938,8 +1445,8 @@
         info->in_event_loop = true;
     }
 
-    pollfd pfd[3];
-    memset(&pfd, 0, 3*sizeof(pfd[0]));
+    pollfd pfd[4];
+    memset(&pfd, 0, 4*sizeof(pfd[0]));
 
     pfd[0].fd = nl_socket_get_fd(info->event_sock);
     pfd[0].events = POLLIN;
@@ -950,14 +1457,19 @@
     pfd[2].fd = info->exit_sockets[1];
     pfd[2].events = POLLIN;
 
+    if(info->wifihal_ctrl_sock.s > 0) {
+      pfd[3].fd = info->wifihal_ctrl_sock.s ;
+      pfd[3].events = POLLIN;
+    }
     /* TODO: Add support for timeouts */
 
     do {
         pfd[0].revents = 0;
         pfd[1].revents = 0;
         pfd[2].revents = 0;
+        pfd[3].revents = 0;
         //ALOGI("Polling sockets");
-        int result = poll(pfd, 3, -1);
+        int result = poll(pfd, 4, -1);
         if (result < 0) {
             ALOGE("Error polling socket");
         } else {
@@ -967,6 +1479,9 @@
             if (pfd[1].revents & (POLLIN | POLLHUP | POLLERR)) {
                 internal_event_handler(handle, pfd[1].revents, info->user_sock);
             }
+            if ((info->wifihal_ctrl_sock.s > 0) && (pfd[3].revents & (POLLIN | POLLHUP | POLLERR))) {
+                internal_event_handler_app(handle, pfd[3].revents, &info->wifihal_ctrl_sock);
+            }
         }
         rb_timerhandler(info);
     } while (!info->clean_up);
@@ -1008,7 +1523,68 @@
             ALOGI("event received %s, vendor_id = 0x%0x, subcmd = 0x%0x",
                   event.get_cmdString(), vendor_id, subcmd);
         }
-    } else {
+    }
+    else if((info->wifihal_ctrl_sock.s > 0) && (cmd == NL80211_CMD_FRAME))
+    {
+       struct genlmsghdr *genlh;
+       struct  nlmsghdr *nlh = nlmsg_hdr(msg);
+       genlh = (struct genlmsghdr *)nlmsg_data(nlh);
+       struct nlattr *nlattrs[NL80211_ATTR_MAX + 1];
+
+       wifihal_ctrl_event_t *ctrl_evt;
+       char *buff;
+       wifihal_mon_sock_t *reg;
+
+       nla_parse(nlattrs, NL80211_ATTR_MAX, genlmsg_attrdata(genlh, 0),
+                 genlmsg_attrlen(genlh, 0), NULL);
+
+       if (!nlattrs[NL80211_ATTR_FRAME])
+       {
+         ALOGD("No Frame body");
+         return WIFI_SUCCESS;
+       }
+
+       ctrl_evt = (wifihal_ctrl_event_t *)malloc(DEFAULT_PAGE_SIZE);
+       if(ctrl_evt == NULL)
+       {
+         ALOGE("Memory allocation failure");
+         return -1;
+       }
+       memset((char *)ctrl_evt, 0, DEFAULT_PAGE_SIZE);
+       ctrl_evt->family_name = GENERIC_NL_FAMILY;
+       ctrl_evt->cmd_id = cmd;
+       ctrl_evt->data_len = msg->nm_nlh->nlmsg_len;
+       memcpy(ctrl_evt->data, (char *)msg->nm_nlh, ctrl_evt->data_len);
+
+
+       buff = (char *)nla_data(nlattrs[NL80211_ATTR_FRAME]) + 24; //! Size of Wlan80211FrameHeader
+
+       list_for_each_entry(reg, &info->monitor_sockets, list) {
+
+                 if(reg == NULL)
+                    break;
+
+                 if (memcmp(reg->match, buff, reg->match_len))
+                     continue;
+
+                 /* found match! */
+                 /* Indicate the received Action frame to respective client */
+                 if (sendto(info->wifihal_ctrl_sock.s, (char *)ctrl_evt,
+                            sizeof(*ctrl_evt) + ctrl_evt->data_len,
+                            0, (struct sockaddr *)&reg->monsock, reg->monsock_len) < 0)
+                 {
+                   int _errno = errno;
+                   ALOGE("socket send failed : %d",_errno);
+
+                   if (_errno == ENOBUFS || _errno == EAGAIN) {
+                   }
+                 }
+
+        }
+        free(ctrl_evt);
+    }
+
+    else {
         ALOGV("event received %s", event.get_cmdString());
     }
 
diff --git a/qcwcn/wifi_hal/wifi_hal_ctrl/wifi_hal_ctrl.c b/qcwcn/wifi_hal/wifi_hal_ctrl/wifi_hal_ctrl.c
new file mode 100644
index 0000000..61834fc
--- /dev/null
+++ b/qcwcn/wifi_hal/wifi_hal_ctrl/wifi_hal_ctrl.c
@@ -0,0 +1,234 @@
+/*
+ * Copyright (c) 2019 The Linux Foundation. All rights reserved.
+ *
+ * wpa_supplicant/hostapd control interface library
+ * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name(s) of the above-listed copyright holder(s) nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "wifi_hal_ctrl.h"
+
+struct wifihal_ctrl * wifihal_ctrl_open2(const char *ctrl_path,
+                                         const char *cli_path)
+{
+    struct wifihal_ctrl *ctrl;
+    static int counter = 0;
+    int retval;
+    size_t res;
+    int tries = 0;
+    int flags;
+#ifdef ANDROID
+    struct group *grp_wifi;
+    gid_t gid_wifi;
+    struct passwd *pwd_system;
+    uid_t uid_system;
+#endif
+
+    if (ctrl_path == NULL)
+        return NULL;
+
+    ctrl = malloc(sizeof(*ctrl));
+    if (ctrl == NULL) {
+        return NULL;
+    }
+
+    memset(ctrl, 0, sizeof(*ctrl));
+    ctrl->s = socket(PF_UNIX, SOCK_DGRAM, 0);
+    if (ctrl->s < 0) {
+         free(ctrl);
+         return NULL;
+    }
+
+    ctrl->local.sun_family = AF_UNIX;
+
+try_again:
+    if (cli_path && cli_path[0] == '/') {
+       res = strlcpy(ctrl->local.sun_path, cli_path,
+                     sizeof(ctrl->local.sun_path));
+
+       if (res >= sizeof(ctrl->local.sun_path)) {
+             close(ctrl->s);
+             free(ctrl);
+             return NULL;
+         }
+
+    } else {
+       counter++;
+       retval = snprintf(ctrl->local.sun_path,
+                      sizeof(ctrl->local.sun_path),
+                      CONFIG_CTRL_IFACE_CLIENT_DIR "/"
+                      CONFIG_CTRL_IFACE_CLIENT_PREFIX "%d-%d",
+                      (int) getpid(), counter);
+    }
+    tries++;
+    if (bind(ctrl->s, (struct sockaddr *) &ctrl->local,
+             sizeof(ctrl->local)) < 0) {
+       if (errno == EADDRINUSE && tries < 2) {
+           /*
+            * getpid() returns unique identifier for this instance
+            * of wifihal_ctrl, so the existing socket file must have
+            * been left by unclean termination of an earlier run.
+            * Remove the file and try again.
+            */
+           unlink(ctrl->local.sun_path);
+           goto try_again;
+        }
+       close(ctrl->s);
+       free(ctrl);
+       return NULL;
+    }
+
+#ifdef ANDROID
+    chmod(ctrl->local.sun_path, S_IRWXU | S_IRWXG );
+
+    /* Set group even if we do not have privileges to change owner */
+    grp_wifi = getgrnam("wifi");
+    gid_wifi = grp_wifi ? grp_wifi->gr_gid : 0;
+    pwd_system = getpwnam("system");
+    uid_system = pwd_system ? pwd_system->pw_uid : 0;
+    if (!gid_wifi || !uid_system) {
+        close(ctrl->s);
+        unlink(ctrl->local.sun_path);
+        free(ctrl);
+        return NULL;
+    }
+    chown(ctrl->local.sun_path, -1, gid_wifi);
+    chown(ctrl->local.sun_path, uid_system, gid_wifi);
+
+
+    if (*ctrl_path != '/') {
+            free(ctrl);
+            return NULL;
+           }
+#endif /* ANDROID */
+
+       ctrl->dest.sun_family = AF_UNIX;
+       res = strlcpy(ctrl->dest.sun_path, ctrl_path,
+                     sizeof(ctrl->dest.sun_path));
+       if (res >= sizeof(ctrl->dest.sun_path)) {
+           close(ctrl->s);
+           free(ctrl);
+           return NULL;
+       }
+       if (connect(ctrl->s, (struct sockaddr *) &ctrl->dest,
+            sizeof(ctrl->dest)) < 0) {
+        close(ctrl->s);
+        unlink(ctrl->local.sun_path);
+        free(ctrl);
+        return NULL;
+       }
+       /*
+        * Make socket non-blocking so that we don't hang forever if
+        * target dies unexpectedly.
+        */
+       flags = fcntl(ctrl->s, F_GETFL);
+       if (flags >= 0) {
+        flags |= O_NONBLOCK;
+        if (fcntl(ctrl->s, F_SETFL, flags) < 0) {
+             perror("fcntl(ctrl->s, O_NONBLOCK)");
+             /* Not fatal, continue on.*/
+        }
+       }
+       return ctrl;
+}
+
+struct wifihal_ctrl * wifihal_ctrl_open(const char *ctrl_path)
+{
+    return wifihal_ctrl_open2(ctrl_path, NULL);
+}
+
+void wifihal_ctrl_close(struct wifihal_ctrl *ctrl)
+{
+    if (ctrl == NULL)
+        return;
+    unlink(ctrl->local.sun_path);
+    if (ctrl->s >= 0)
+        close(ctrl->s);
+    free(ctrl);
+}
+
+int wifihal_ctrl_request(struct wifihal_ctrl *ctrl, const char *cmd, size_t cmd_len,
+                         char *reply, size_t *reply_len)
+{
+    struct timeval tv;
+    int counter = 0, res;
+    fd_set rfds;
+    const char *_cmd;
+    size_t _cmd_len;
+    char *cmd_buf = NULL;
+
+    _cmd = cmd;
+    _cmd_len = cmd_len;
+
+    errno = 0;
+retry_send:
+    if (sendto(ctrl->s, _cmd, _cmd_len, 0, (struct sockaddr *)&ctrl->dest, sizeof(ctrl->dest)) < 0) {
+        if (errno == EAGAIN || errno == EBUSY || errno == EWOULDBLOCK)
+        {
+          /*
+           * Must be a non-blocking socket... Try for a bit
+           * longer before giving up.
+           */
+              if(counter == 5) {
+                 goto send_err;
+                } else {
+                 counter++;
+                }
+                sleep(1);
+                goto retry_send;
+         }
+         send_err:
+         free(cmd_buf);
+         return -1;
+     }
+     free(cmd_buf);
+
+     for (;;) {
+        tv.tv_sec = 10;
+        tv.tv_usec = 0;
+        FD_ZERO(&rfds);
+        FD_SET(ctrl->s, &rfds);
+        res = select(ctrl->s + 1, &rfds, NULL, NULL, &tv);
+        if (res < 0 && errno == EINTR)
+            continue;
+        if (res < 0)
+            return res;
+        if (FD_ISSET(ctrl->s, &rfds)) {
+                res = recv(ctrl->s, reply, *reply_len, 0);
+                if (res < 0)
+                    return res;
+                *reply_len = res;
+                break;
+        } else {
+           return -2;
+        }
+     }
+     return 0;
+}
diff --git a/qcwcn/wifi_hal/wifi_hal_ctrl/wifi_hal_ctrl.h b/qcwcn/wifi_hal/wifi_hal_ctrl/wifi_hal_ctrl.h
new file mode 100644
index 0000000..dbfba56
--- /dev/null
+++ b/qcwcn/wifi_hal/wifi_hal_ctrl/wifi_hal_ctrl.h
@@ -0,0 +1,225 @@
+/*
+ * Copyright (c) 2019 The Linux Foundation. All rights reserved.
+ *
+ * wpa_supplicant/hostapd control interface library
+ * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name(s) of the above-listed copyright holder(s) nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef WIFIHAL_CTRL_H
+#define WIFIHAL_CTRL_H
+
+#include <sys/un.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "stdlib.h"
+
+#ifdef ANDROID
+#include <dirent.h>
+#include <grp.h>
+#include <pwd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <cutils/sockets.h>
+#endif /* ANDROID */
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+/**
+ * struct wifihal_ctrl - Internal structure for control interface library
+ *
+ * This structure is used by the clients to interface with WiFi Hal
+ * library to store internal data. Programs using the library should not touch
+ * this data directly. They can only use the pointer to the data structure as
+ * an identifier for the control interface connection and use this as one of
+ * the arguments for most of the control interface library functions.
+ */
+
+struct wifihal_ctrl {
+    int s;
+    struct sockaddr_un local;
+    struct sockaddr_un dest;
+};
+
+#ifndef CONFIG_CTRL_IFACE_CLIENT_DIR
+#define CONFIG_CTRL_IFACE_CLIENT_DIR "/dev/socket/wifihal"
+#endif /* CONFIG_CTRL_IFACE_CLIENT_DIR */
+#ifndef CONFIG_CTRL_IFACE_CLIENT_PREFIX
+#define CONFIG_CTRL_IFACE_CLIENT_PREFIX "wifihal_ctrl_cli_"
+#endif /* CONFIG_CTRL_IFACE_CLIENT_PREFIX */
+
+#define DEFAULT_PAGE_SIZE         4096
+
+enum nl_family_type
+{
+  //! gen netlink family
+  GENERIC_NL_FAMILY = 1,
+  //! Cld80211 family
+  CLD80211_FAMILY
+};
+
+
+enum wifihal_ctrl_cmd
+{
+    /** attach monitor sock */
+    WIFIHAL_CTRL_MONITOR_ATTACH,
+    /** dettach monitor sock */
+    WIFIHAL_CTRL_MONITOR_DETTACH,
+    /** Send data over Netlink Sock */
+    WIFIHAL_CTRL_SEND_NL_DATA,
+};
+
+//! WIFIHAL Control Request
+typedef struct wifihal_ctrl_req_s {
+    //! ctrl command
+    uint32_t ctrl_cmd;
+    //! Family name
+    uint32_t family_name;
+    //! command ID
+    uint32_t cmd_id;
+    //! monitor sock len
+    uint32_t monsock_len;
+    //! monitor sock
+    struct sockaddr_un monsock;
+    //! data buff length
+    uint32_t data_len;
+    //! reserved
+    uint32_t reserved[4];
+    //! data
+    char data[0];
+}wifihal_ctrl_req_t;
+
+
+//! WIFIHAL Sync Response
+typedef struct wifihal_ctrl_sync_rsp_s {
+    //! ctrl command
+    uint32_t ctrl_cmd;
+    //! Family name
+    uint32_t family_name;
+    //! command ID
+    uint32_t cmd_id;
+    //! status for the request
+    int status;
+    //! reserved
+    uint32_t reserved[4];
+}wifihal_ctrl_sync_rsp_t;
+
+//! WIFIHAL Async Response
+typedef struct wifihal_ctrl_event_s {
+    //! Family name
+    uint32_t family_name;
+    //! command ID
+    uint32_t cmd_id;
+    //! data buff length
+    uint32_t data_len;
+    //! reserved
+    uint32_t reserved;
+    //! data
+    char data[0];
+}wifihal_ctrl_event_t;
+
+/* WiFi Hal control interface access */
+
+/**
+ * wifihal_ctrl_open - Open a control interface to WiFi-Hal
+ * @ctrl_path: Path for UNIX domain sockets; ignored if UDP sockets are used.
+ * Returns: Pointer to abstract control interface data or %NULL on failure
+ *
+ * This function is used to open a control interface to WiFi-Hal.
+ * ctrl_path is usually /var/run/wifihal. This path
+ * is configured in WiFi-Hal and other programs using the control
+ * interface need to use matching path configuration.
+ */
+struct wifihal_ctrl * wifihal_ctrl_open(const char *ctrl_path);
+
+/**
+ * wifihal_ctrl_open2 - Open a control interface to wifihal
+ * @ctrl_path: Path for UNIX domain sockets; ignored if UDP sockets are used.
+ * @cli_path: Path for client UNIX domain sockets; ignored if UDP socket
+ *            is used.
+ * Returns: Pointer to abstract control interface data or %NULL on failure
+ *
+ * This function is used to open a control interface to wifihal
+ * when the socket path for client need to be specified explicitly. Default
+ * ctrl_path is usually /var/run/wifihal and client
+ * socket path is /tmp.
+ */
+struct wifihal_ctrl * wifihal_ctrl_open2(const char *ctrl_path, const char *cli_path);
+
+
+/**
+ * wifihal_ctrl_close - Close a control interface to wifihal
+ * @ctrl: Control interface data from wifihal_ctrl_open()
+ *
+ * This function is used to close a control interface.
+ */
+void wifihal_ctrl_close(struct wifihal_ctrl *ctrl);
+
+
+/**
+ * wifihal_ctrl_request - Send a command to wifihal
+ * @ctrl: Control interface data from wifihal_ctrl_open()
+ * @cmd: Command; usually, ASCII text, e.g., "PING"
+ * @cmd_len: Length of the cmd in bytes
+ * @reply: Buffer for the response
+ * @reply_len: Reply buffer length
+ * @msg_cb: Callback function for unsolicited messages or %NULL if not used
+ * Returns: 0 on success, -1 on error (send or receive failed), -2 on timeout
+ *
+ * This function is used to send commands to wifihal. Received
+ * response will be written to reply and reply_len is set to the actual length
+ * of the reply. This function will block for up to two seconds while waiting
+ * for the reply. If unsolicited messages are received, the blocking time may
+ * be longer.
+ *
+ * msg_cb can be used to register a callback function that will be called for
+ * unsolicited messages received while waiting for the command response. These
+ * messages may be received if wifihal_ctrl_request() is called at the same time as
+ * wifihal is sending such a message.
+ * FIXME : Change the comment below.
+ * This can happen only if
+ * the program has used wpa_ctrl_attach() to register itself as a monitor for
+ * event messages. Alternatively to msg_cb, programs can register two control
+ * interface connections and use one of them for commands and the other one for
+ * receiving event messages, in other words, call wpa_ctrl_attach() only for
+ * the control interface connection that will be used for event messages.
+ */
+int wifihal_ctrl_request(struct wifihal_ctrl *ctrl, const char *cmd, size_t cmd_len,
+                         char *reply, size_t *reply_len);
+
+
+#ifdef  __cplusplus
+}
+#endif
+
+#endif
diff --git a/qcwcn/wifi_hal/wifihal_internal.h b/qcwcn/wifi_hal/wifihal_internal.h
index 986b151..5cc78c3 100755
--- a/qcwcn/wifi_hal/wifihal_internal.h
+++ b/qcwcn/wifi_hal/wifihal_internal.h
@@ -35,6 +35,8 @@
  */
 
 #include "wifi_hal.h"
+#include "list.h"
+#include "sys/un.h"
 
 #define WIFIHAL_LOWI_MAJOR_VERSION      2
 #define WIFIHAL_LOWI_MINOR_VERSION      1
@@ -45,6 +47,25 @@
 #define DUAL_SIDED_RANGING_SUPPORED   0x00000002
 #define GSCAN_SUPPORTED               0x00000004
 
+typedef struct wifihal_mon_sock_s {
+
+  struct list_head list;
+  //! Family name
+  uint32_t family_name;
+  //! command ID
+  uint32_t cmd_id;
+  //! monitor sock len
+  uint32_t monsock_len;
+  //! monitor sock
+  struct sockaddr_un monsock;
+  //! match buff length
+  uint32_t match_len;
+  //! reserved byte
+  uint32_t reserved[4];
+  //! match buff
+  char match[0];
+} wifihal_mon_sock_t;
+
 /*
  * This structure is a table of function pointers to the functions
  * used by the wifihal to interface with LOWI
diff --git a/qcwcn/wifi_hal/wifilogger_diag.cpp b/qcwcn/wifi_hal/wifilogger_diag.cpp
index 4dd9810..436a42b 100644
--- a/qcwcn/wifi_hal/wifilogger_diag.cpp
+++ b/qcwcn/wifi_hal/wifilogger_diag.cpp
@@ -44,6 +44,8 @@
 #include "wifilogger_diag.h"
 #include "wifilogger_vendor_tag_defs.h"
 #include "pkt_stats.h"
+#include <errno.h>
+#include "wifi_hal_ctrl.h"
 
 static uint32_t get_le32(const uint8_t *pos)
 {
@@ -2463,52 +2465,62 @@
 static wifi_error parse_stats(hal_info *info, u8 *data, u32 buflen)
 {
     wh_pktlog_hdr_t *pkt_stats_header;
-    wh_pktlog_hdr_v2_t *pkt_stats_header_t;
+    wh_pktlog_hdr_v2_t *pkt_stats_header_v2_t;
     wifi_error status = WIFI_SUCCESS;
 
     do {
+        u32 record_len;
+
         if (buflen < sizeof(wh_pktlog_hdr_t)) {
             status = WIFI_ERROR_INVALID_ARGS;
             break;
         }
 
         pkt_stats_header = (wh_pktlog_hdr_t *)data;
+        pkt_stats_header_v2_t = (wh_pktlog_hdr_v2_t *)data;
 
-        if (buflen < (sizeof(wh_pktlog_hdr_t) + pkt_stats_header->size)) {
+        if (info->pkt_log_ver == PKT_LOG_V2) {
+            if (buflen < sizeof(wh_pktlog_hdr_v2_t)) {
+                status = WIFI_ERROR_INVALID_ARGS;
+                break;
+            }
+            record_len = (sizeof(wh_pktlog_hdr_v2_t) + pkt_stats_header_v2_t->size);
+        } else {
+            if (pkt_stats_header->flags & PKT_INFO_FLG_PKT_DUMP_V2){
+                if (buflen < sizeof(wh_pktlog_hdr_v2_t)) {
+                    status = WIFI_ERROR_INVALID_ARGS;
+                    break;
+                }
+                record_len = (sizeof(wh_pktlog_hdr_v2_t) + pkt_stats_header_v2_t->size);
+            } else {
+                record_len = (sizeof(wh_pktlog_hdr_t) + pkt_stats_header->size);
+            }
+        }
+
+        if (buflen < record_len) {
             status = WIFI_ERROR_INVALID_ARGS;
             break;
         }
         /* Pkt_log_V2 based packet parsing */
         if (info->pkt_log_ver == PKT_LOG_V2) {
-           pkt_stats_header_t = (wh_pktlog_hdr_v2_t *)data;
-           status = parse_stats_record_v2(info, pkt_stats_header_t);
-           if (status != WIFI_SUCCESS) {
-               ALOGE("Failed to parse the stats type : %d",
-                     pkt_stats_header_t->log_type);
-               return status;
-           }
+            status = parse_stats_record_v2(info, pkt_stats_header_v2_t);
+            if (status != WIFI_SUCCESS) {
+                ALOGE("Failed to parse the stats type : %d",
+                     pkt_stats_header_v2_t->log_type);
+                return status;
+            }
         /* Pkt_log_V1 based packet parsing */
         } else {
-           status = parse_stats_record_v1(info, pkt_stats_header);
-           if (status != WIFI_SUCCESS) {
-               ALOGE("Failed to parse the stats type : %d",
+            status = parse_stats_record_v1(info, pkt_stats_header);
+            if (status != WIFI_SUCCESS) {
+                ALOGE("Failed to parse the stats type : %d",
                      pkt_stats_header->log_type);
-               return status;
-           }
+                return status;
+            }
         }
+        data += record_len;
+        buflen -= record_len;
 
-        if (info->pkt_log_ver == PKT_LOG_V2) {
-            data += (sizeof(wh_pktlog_hdr_v2_t) + pkt_stats_header->size);
-            buflen -= (sizeof(wh_pktlog_hdr_v2_t) + pkt_stats_header->size);
-        } else {
-           if (pkt_stats_header->flags & PKT_INFO_FLG_PKT_DUMP_V2){
-               data += (sizeof(wh_pktlog_hdr_v2_t) + pkt_stats_header->size);
-               buflen -= (sizeof(wh_pktlog_hdr_v2_t) + pkt_stats_header->size);
-           } else {
-               data += (sizeof(wh_pktlog_hdr_t) + pkt_stats_header->size);
-               buflen -= (sizeof(wh_pktlog_hdr_t) + pkt_stats_header->size);
-           }
-        }
     } while (buflen > 0);
 
     return status;
@@ -2568,7 +2580,9 @@
         genlh = (struct genlmsghdr *)nlmsg_data(nlh);
         if (genlh->cmd == ANI_NL_MSG_PUMAC ||
             genlh->cmd == ANI_NL_MSG_LOG ||
-            genlh->cmd == ANI_NL_MSG_CNSS_DIAG) {
+            genlh->cmd == ANI_NL_MSG_CNSS_DIAG ||
+            genlh->cmd == WLAN_NL_MSG_OEM)
+        {
             cmd = genlh->cmd;
             int result = nla_parse(attrs, CLD80211_ATTR_MAX, genlmsg_attrdata(genlh, 0),
                     genlmsg_attrlen(genlh, 0), NULL);
@@ -2586,6 +2600,59 @@
                 ALOGE("Invalid data received from driver");
                 return WIFI_SUCCESS;
             }
+            if((info->wifihal_ctrl_sock.s > 0) && (genlh->cmd == WLAN_NL_MSG_OEM)) {
+               wifihal_ctrl_event_t *ctrl_evt;
+               wifihal_mon_sock_t *reg;
+
+               ctrl_evt = (wifihal_ctrl_event_t *)malloc(sizeof(*ctrl_evt) + nlh->nlmsg_len);
+
+               if(ctrl_evt == NULL)
+               {
+                 ALOGE("Memory allocation failure");
+                 return WIFI_ERROR_OUT_OF_MEMORY;
+               }
+               memset((char *)ctrl_evt, 0, sizeof(*ctrl_evt) + nlh->nlmsg_len);
+
+               ctrl_evt->family_name = CLD80211_FAMILY;
+               ctrl_evt->cmd_id = WLAN_NL_MSG_OEM;
+               ctrl_evt->data_len = nlh->nlmsg_len;
+               memcpy(ctrl_evt->data, (char *)nlh,  ctrl_evt->data_len);
+
+               //! Send oem data to all the registered clients
+
+               list_for_each_entry(reg, &info->monitor_sockets, list) {
+
+                   if(reg == NULL)
+                      break;
+
+                   if (reg->family_name != CLD80211_FAMILY || reg->cmd_id != WLAN_NL_MSG_OEM)
+                       continue;
+
+                   /* found match! */
+                   /* Indicate the received OEM msg to respective client
+                      it is responsibility of the registered client to check
+                      the oem_msg is meant for them or not based on oem_msg sub type */
+                   if (sendto(info->wifihal_ctrl_sock.s, (char *)ctrl_evt,
+                              sizeof(*ctrl_evt) + ctrl_evt->data_len, 0,
+                              (struct sockaddr *)&reg->monsock, reg->monsock_len) < 0)
+                   {
+                     int _errno = errno;
+                     ALOGE("socket send failed : %d",_errno);
+
+                     if (_errno == ENOBUFS || _errno == EAGAIN) {
+                         /*
+                          * The socket send buffer could be full. This
+                          * may happen if client programs are not
+                          * receiving their pending messages. Close and
+                          * reopen the socket as a workaround to avoid
+                          * getting stuck being unable to send any new
+                          * responses.
+                          */
+                     }
+                   }
+               }
+               free(ctrl_evt);
+            }
         }
     } else {
         wnl = (tAniNlHdr *)nlmsg_hdr(msg);
diff --git a/qcwcn/wifi_hal/wifilogger_diag.h b/qcwcn/wifi_hal/wifilogger_diag.h
index 24ad1eb..fc23cb7 100644
--- a/qcwcn/wifi_hal/wifilogger_diag.h
+++ b/qcwcn/wifi_hal/wifilogger_diag.h
@@ -104,7 +104,10 @@
 }__attribute__((packed)) dbglog_slot;
 
 typedef enum eAniNlModuleTypes {
-    ANI_NL_MSG_PUMAC = ANI_NL_MSG_BASE + 0x01,
+    ANI_NL_MSG_PUMAC = ANI_NL_MSG_BASE + 0x01,// PTT Socket App
+    ANI_NL_MSG_PTT = ANI_NL_MSG_BASE + 0x07,// Quarky GUI
+    WLAN_NL_MSG_BTC,
+    WLAN_NL_MSG_OEM,
     ANI_NL_MSG_CNSS_DIAG = ANI_NL_MSG_BASE + 0x0B,
     ANI_NL_MSG_LOG,
     ANI_NL_MSG_MAX