prima: Support for NAT keep alive packets

qcacld-2.0 to pronto propagation

This is the host driver implementation to support sending
fixed NAT keep alive packets to WiFi Network.
1. WifiHal issues start send packet with ip_packet, ip_packet_len,
destination mac address and period. Host driver populates the data
and sends WMI message to firmware to configure the tx pattern.
2. WifiHal upon sending stop packet, host driver sends delete
tx pattern to the firmware.

Additional changes are done to pass the pattern bitmap to WDA.

Change-Id: I58309d1c58ff0ba9bcf86c31ac2a7eccfa3a7f8f
CRs-Fixed: 915690
diff --git a/CORE/HDD/inc/wlan_hdd_cfg80211.h b/CORE/HDD/inc/wlan_hdd_cfg80211.h
index f4072f1..f6554af 100644
--- a/CORE/HDD/inc/wlan_hdd_cfg80211.h
+++ b/CORE/HDD/inc/wlan_hdd_cfg80211.h
@@ -179,6 +179,8 @@
 
     QCA_NL80211_VENDOR_SUBCMD_MONITOR_RSSI = 80,
 
+    QCA_NL80211_VENDOR_SUBCMD_OFFLOADED_PACKETS = 79,
+
     QCA_NL80211_VENDOR_SUBCMD_SETBAND = 105,
 };
 
@@ -1170,6 +1172,50 @@
                 QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_AFTER_LAST - 1,
 };
 
+#ifdef WLAN_FEATURE_OFFLOAD_PACKETS
+/**
+ * enum wlan_offloaded_packets_control - control commands
+ * @WLAN_START_OFFLOADED_PACKETS: start offloaded packets
+ * @WLAN_STOP_OFFLOADED_PACKETS: stop offloaded packets
+ *
+ */
+enum wlan_offloaded_packets_control
+{
+    WLAN_START_OFFLOADED_PACKETS = 1,
+    WLAN_STOP_OFFLOADED_PACKETS  = 2
+};
+
+/**
+ * enum qca_wlan_vendor_attr_offloaded_packets - offloaded packets
+ * @QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_INVALID: invalid
+ * @QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_SENDING_CONTROL: control
+ * @QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_REQUEST_ID: request id
+ * @QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_IP_PACKET_DATA: ip packet data
+ * @QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_SRC_MAC_ADDR: src mac address
+ * @QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_DST_MAC_ADDR: destination mac address
+ * @QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_PERIOD: period in milli seconds
+ * @QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_AFTER_LAST: after last
+ * @QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_MAX: max
+ */
+enum qca_wlan_vendor_attr_offloaded_packets
+{
+    QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_INVALID = 0,
+    QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_SENDING_CONTROL,
+    QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_REQUEST_ID,
+
+    /* Packet in hex format */
+    QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_IP_PACKET_DATA,
+    QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_SRC_MAC_ADDR,
+    QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_DST_MAC_ADDR,
+    QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_PERIOD,
+
+    /* keep last */
+    QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_AFTER_LAST,
+    QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_MAX =
+    QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_AFTER_LAST - 1,
+};
+#endif
+
 struct cfg80211_bss* wlan_hdd_cfg80211_update_bss_db( hdd_adapter_t *pAdapter,
                                       tCsrRoamInfo *pRoamInfo
                                       );
diff --git a/CORE/HDD/inc/wlan_hdd_main.h b/CORE/HDD/inc/wlan_hdd_main.h
index ebb2a9c..424c852 100644
--- a/CORE/HDD/inc/wlan_hdd_main.h
+++ b/CORE/HDD/inc/wlan_hdd_main.h
@@ -1263,6 +1263,31 @@
 };
 #endif /* End of WLAN_FEATURE_EXTSCAN */
 
+#ifdef WLAN_FEATURE_OFFLOAD_PACKETS
+/**
+ * struct hdd_offloaded_packets - request id to pattern id mapping
+ * @request_id: request id
+ * @pattern_id: pattern id
+ *
+ */
+struct hdd_offloaded_packets
+{
+    uint32_t request_id;
+    uint8_t  pattern_id;
+};
+
+/**
+ * struct hdd_offloaded_packets_ctx - offloaded packets context
+ * @op_table: request id to pattern id table
+ * @op_lock: mutex lock
+ */
+struct hdd_offloaded_packets_ctx
+{
+    struct hdd_offloaded_packets op_table[MAXNUM_PERIODIC_TX_PTRNS];
+    struct mutex op_lock;
+};
+#endif
+
 /** Adapter stucture definition */
 
 struct hdd_context_s
@@ -1498,6 +1523,9 @@
     unsigned long  last_suspend_success;
     v_U32_t  continuous_suspend_fail_cnt;
 
+#ifdef WLAN_FEATURE_OFFLOAD_PACKETS
+    struct hdd_offloaded_packets_ctx op_ctx;
+#endif
 };
 
 
diff --git a/CORE/HDD/inc/wlan_hdd_tx_rx.h b/CORE/HDD/inc/wlan_hdd_tx_rx.h
index 5fab180..68f7174 100644
--- a/CORE/HDD/inc/wlan_hdd_tx_rx.h
+++ b/CORE/HDD/inc/wlan_hdd_tx_rx.h
@@ -76,6 +76,8 @@
 #define SME_QOS_UAPSD_CFG_VI_CHANGED_MASK     0xF4
 #define SME_QOS_UAPSD_CFG_VO_CHANGED_MASK     0xF8
 
+#define HDD_ETH_HEADER_LEN      14
+
 /* WLAN_DHCP_DEBUG */
 #define RX_PATH     ( 0 )
 #define TX_PATH     ( 1 )
diff --git a/CORE/HDD/src/wlan_hdd_cfg80211.c b/CORE/HDD/src/wlan_hdd_cfg80211.c
index 9840645..b74cb3c 100644
--- a/CORE/HDD/src/wlan_hdd_cfg80211.c
+++ b/CORE/HDD/src/wlan_hdd_cfg80211.c
@@ -6570,6 +6570,433 @@
     return ret;
 }
 
+#ifdef WLAN_FEATURE_OFFLOAD_PACKETS
+/**
+ * hdd_map_req_id_to_pattern_id() - map request id to pattern id
+ * @hdd_ctx: HDD context
+ * @request_id: [input] request id
+ * @pattern_id: [output] pattern id
+ *
+ * This function loops through request id to pattern id array
+ * if the slot is available, store the request id and return pattern id
+ * if entry exists, return the pattern id
+ *
+ * Return: 0 on success and errno on failure
+ */
+static int hdd_map_req_id_to_pattern_id(hdd_context_t *hdd_ctx,
+        uint32_t request_id,
+        uint8_t *pattern_id)
+{
+    uint32_t i;
+
+    mutex_lock(&hdd_ctx->op_ctx.op_lock);
+    for (i = 0; i < MAXNUM_PERIODIC_TX_PTRNS; i++)
+    {
+        if (hdd_ctx->op_ctx.op_table[i].request_id == 0)
+        {
+            hdd_ctx->op_ctx.op_table[i].request_id = request_id;
+            *pattern_id = hdd_ctx->op_ctx.op_table[i].pattern_id;
+            mutex_unlock(&hdd_ctx->op_ctx.op_lock);
+            return 0;
+        } else if (hdd_ctx->op_ctx.op_table[i].request_id ==
+                request_id) {
+            *pattern_id = hdd_ctx->op_ctx.op_table[i].pattern_id;
+            mutex_unlock(&hdd_ctx->op_ctx.op_lock);
+            return 0;
+        }
+    }
+    mutex_unlock(&hdd_ctx->op_ctx.op_lock);
+    return -EINVAL;
+}
+
+/**
+ * hdd_unmap_req_id_to_pattern_id() - unmap request id to pattern id
+ * @hdd_ctx: HDD context
+ * @request_id: [input] request id
+ * @pattern_id: [output] pattern id
+ *
+ * This function loops through request id to pattern id array
+ * reset request id to 0 (slot available again) and
+ * return pattern id
+ *
+ * Return: 0 on success and errno on failure
+ */
+static int hdd_unmap_req_id_to_pattern_id(hdd_context_t *hdd_ctx,
+        uint32_t request_id,
+        uint8_t *pattern_id)
+{
+    uint32_t i;
+
+    mutex_lock(&hdd_ctx->op_ctx.op_lock);
+    for (i = 0; i < MAXNUM_PERIODIC_TX_PTRNS; i++)
+    {
+        if (hdd_ctx->op_ctx.op_table[i].request_id == request_id)
+        {
+            hdd_ctx->op_ctx.op_table[i].request_id = 0;
+            *pattern_id = hdd_ctx->op_ctx.op_table[i].pattern_id;
+            mutex_unlock(&hdd_ctx->op_ctx.op_lock);
+            return 0;
+        }
+    }
+    mutex_unlock(&hdd_ctx->op_ctx.op_lock);
+    return -EINVAL;
+}
+
+
+/*
+ * define short names for the global vendor params
+ * used by __wlan_hdd_cfg80211_offloaded_packets()
+ */
+#define PARAM_MAX QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_MAX
+#define PARAM_REQUEST_ID \
+    QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_REQUEST_ID
+#define PARAM_CONTROL \
+    QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_SENDING_CONTROL
+#define PARAM_IP_PACKET \
+    QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_IP_PACKET_DATA
+#define PARAM_SRC_MAC_ADDR \
+    QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_SRC_MAC_ADDR
+#define PARAM_DST_MAC_ADDR \
+    QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_DST_MAC_ADDR
+#define PARAM_PERIOD QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_PERIOD
+
+/**
+ * wlan_hdd_add_tx_ptrn() - add tx pattern
+ * @adapter: adapter pointer
+ * @hdd_ctx: hdd context
+ * @tb: nl attributes
+ *
+ * This function reads the NL attributes and forms a AddTxPtrn message
+ * posts it to SME.
+ *
+ */
+static int
+wlan_hdd_add_tx_ptrn(hdd_adapter_t *adapter, hdd_context_t *hdd_ctx,
+        struct nlattr **tb)
+{
+    struct sSirAddPeriodicTxPtrn *add_req;
+    eHalStatus status;
+    uint32_t request_id, ret, len;
+    uint8_t pattern_id = 0;
+    v_MACADDR_t dst_addr;
+    uint16_t eth_type = htons(ETH_P_IP);
+
+    if (!hdd_connIsConnected(WLAN_HDD_GET_STATION_CTX_PTR(adapter)))
+    {
+        hddLog(LOGE, FL("Not in Connected state!"));
+        return -ENOTSUPP;
+    }
+
+    add_req = vos_mem_malloc(sizeof(*add_req));
+    if (!add_req)
+    {
+        hddLog(LOGE, FL("memory allocation failed"));
+        return -ENOMEM;
+    }
+
+    /* Parse and fetch request Id */
+    if (!tb[PARAM_REQUEST_ID])
+    {
+        hddLog(LOGE, FL("attr request id failed"));
+        goto fail;
+    }
+
+    request_id = nla_get_u32(tb[PARAM_REQUEST_ID]);
+    hddLog(LOG1, FL("Request Id: %u"), request_id);
+    if (request_id == 0)
+    {
+        hddLog(LOGE, FL("request_id cannot be zero"));
+        return -EINVAL;
+    }
+
+    if (!tb[PARAM_PERIOD])
+    {
+        hddLog(LOGE, FL("attr period failed"));
+        goto fail;
+    }
+    add_req->usPtrnIntervalMs = nla_get_u32(tb[PARAM_PERIOD]);
+    hddLog(LOG1, FL("Period: %u ms"), add_req->usPtrnIntervalMs);
+    if (add_req->usPtrnIntervalMs == 0)
+    {
+        hddLog(LOGE, FL("Invalid interval zero, return failure"));
+        goto fail;
+    }
+
+    if (!tb[PARAM_SRC_MAC_ADDR])
+    {
+        hddLog(LOGE, FL("attr source mac address failed"));
+        goto fail;
+    }
+    nla_memcpy(add_req->macAddress, tb[PARAM_SRC_MAC_ADDR],
+            VOS_MAC_ADDR_SIZE);
+    hddLog(LOG1, "input src mac address: "MAC_ADDRESS_STR,
+            MAC_ADDR_ARRAY(add_req->macAddress));
+
+    if (memcmp(add_req->macAddress, adapter->macAddressCurrent.bytes,
+                VOS_MAC_ADDR_SIZE))
+    {
+        hddLog(LOGE,
+            FL("input src mac address and connected ap bssid are different"));
+        goto fail;
+    }
+
+    if (!tb[PARAM_DST_MAC_ADDR])
+    {
+        hddLog(LOGE, FL("attr dst mac address failed"));
+        goto fail;
+    }
+    nla_memcpy(dst_addr.bytes, tb[PARAM_DST_MAC_ADDR], VOS_MAC_ADDR_SIZE);
+    hddLog(LOG1, "input dst mac address: "MAC_ADDRESS_STR,
+            MAC_ADDR_ARRAY(dst_addr.bytes));
+
+    if (!tb[PARAM_IP_PACKET])
+    {
+        hddLog(LOGE, FL("attr ip packet failed"));
+        goto fail;
+    }
+    add_req->ucPtrnSize = nla_len(tb[PARAM_IP_PACKET]);
+    hddLog(LOG1, FL("IP packet len: %u"), add_req->ucPtrnSize);
+
+    if (add_req->ucPtrnSize < 0 ||
+            add_req->ucPtrnSize > (PERIODIC_TX_PTRN_MAX_SIZE -
+                HDD_ETH_HEADER_LEN))
+    {
+        hddLog(LOGE, FL("Invalid IP packet len: %d"),
+                add_req->ucPtrnSize);
+        goto fail;
+    }
+
+    len = 0;
+    vos_mem_copy(&add_req->ucPattern[0], dst_addr.bytes, VOS_MAC_ADDR_SIZE);
+    len += VOS_MAC_ADDR_SIZE;
+    vos_mem_copy(&add_req->ucPattern[len], add_req->macAddress,
+            VOS_MAC_ADDR_SIZE);
+    len += VOS_MAC_ADDR_SIZE;
+    vos_mem_copy(&add_req->ucPattern[len], &eth_type, 2);
+    len += 2;
+
+    /*
+     * This is the IP packet, add 14 bytes Ethernet (802.3) header
+     * ------------------------------------------------------------
+     * | 14 bytes Ethernet (802.3) header | IP header and payload |
+     * ------------------------------------------------------------
+     */
+    vos_mem_copy(&add_req->ucPattern[len],
+            nla_data(tb[PARAM_IP_PACKET]),
+            add_req->ucPtrnSize);
+    add_req->ucPtrnSize += len;
+
+    VOS_TRACE_HEX_DUMP(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
+            add_req->ucPattern, add_req->ucPtrnSize);
+
+    ret = hdd_map_req_id_to_pattern_id(hdd_ctx, request_id, &pattern_id);
+    if (ret)
+    {
+        hddLog(LOGW, FL("req id to pattern id failed (ret=%d)"), ret);
+        goto fail;
+    }
+    add_req->ucPtrnId = pattern_id;
+    hddLog(LOG1, FL("pattern id: %d"), add_req->ucPtrnId);
+
+    status = sme_AddPeriodicTxPtrn(hdd_ctx->hHal, add_req);
+    if (!HAL_STATUS_SUCCESS(status))
+    {
+        hddLog(LOGE,
+                FL("sme_AddPeriodicTxPtrn failed (err=%d)"), status);
+        goto fail;
+    }
+
+    EXIT();
+    vos_mem_free(add_req);
+    return 0;
+
+fail:
+    vos_mem_free(add_req);
+    return -EINVAL;
+}
+
+/**
+ * wlan_hdd_del_tx_ptrn() - delete tx pattern
+ * @adapter: adapter pointer
+ * @hdd_ctx: hdd context
+ * @tb: nl attributes
+ *
+ * This function reads the NL attributes and forms a DelTxPtrn message
+ * posts it to SME.
+ *
+ */
+static int
+wlan_hdd_del_tx_ptrn(hdd_adapter_t *adapter, hdd_context_t *hdd_ctx,
+        struct nlattr **tb)
+{
+    struct sSirDelPeriodicTxPtrn *del_req;
+    eHalStatus status;
+    uint32_t request_id, ret;
+    uint8_t pattern_id = 0;
+
+    /* Parse and fetch request Id */
+    if (!tb[PARAM_REQUEST_ID])
+    {
+        hddLog(LOGE, FL("attr request id failed"));
+        return -EINVAL;
+    }
+    request_id = nla_get_u32(tb[PARAM_REQUEST_ID]);
+    if (request_id == 0)
+    {
+        hddLog(LOGE, FL("request_id cannot be zero"));
+        return -EINVAL;
+    }
+
+    ret = hdd_unmap_req_id_to_pattern_id(hdd_ctx, request_id, &pattern_id);
+    if (ret)
+    {
+        hddLog(LOGW, FL("req id to pattern id failed (ret=%d)"), ret);
+        return -EINVAL;
+    }
+
+    del_req = vos_mem_malloc(sizeof(*del_req));
+    if (!del_req)
+    {
+        hddLog(LOGE, FL("memory allocation failed"));
+        return -ENOMEM;
+    }
+
+    vos_mem_set(del_req, sizeof(*del_req), 0);
+    vos_mem_copy(del_req->macAddress, adapter->macAddressCurrent.bytes,
+            VOS_MAC_ADDR_SIZE);
+    hddLog(LOG1, MAC_ADDRESS_STR, MAC_ADDR_ARRAY(del_req->macAddress));
+    del_req->ucPatternIdBitmap |= (0x1 << pattern_id);
+    hddLog(LOG1, FL("Request Id: %u Pattern id: %d, bitmap %04x"),
+         request_id, pattern_id, del_req->ucPatternIdBitmap);
+
+    status = sme_DelPeriodicTxPtrn(hdd_ctx->hHal, del_req);
+    if (!HAL_STATUS_SUCCESS(status))
+    {
+        hddLog(LOGE,
+                FL("sme_DelPeriodicTxPtrn failed (err=%d)"), status);
+        goto fail;
+    }
+
+    EXIT();
+    vos_mem_free(del_req);
+    return 0;
+
+fail:
+    vos_mem_free(del_req);
+    return -EINVAL;
+}
+
+
+/**
+ * __wlan_hdd_cfg80211_offloaded_packets() - send offloaded packets
+ * @wiphy: Pointer to wireless phy
+ * @wdev: Pointer to wireless device
+ * @data: Pointer to data
+ * @data_len: Data length
+ *
+ * Return: 0 on success, negative errno on failure
+ */
+static int
+__wlan_hdd_cfg80211_offloaded_packets(struct wiphy *wiphy,
+        struct wireless_dev *wdev,
+        const void *data,
+        int data_len)
+{
+    struct net_device *dev = wdev->netdev;
+    hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
+    hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
+    struct nlattr *tb[PARAM_MAX + 1];
+    uint8_t control;
+    int ret;
+    static const struct nla_policy policy[PARAM_MAX + 1] =
+    {
+        [PARAM_REQUEST_ID] = { .type = NLA_U32 },
+        [PARAM_CONTROL] = { .type = NLA_U32 },
+        [PARAM_SRC_MAC_ADDR] = { .type = NLA_BINARY,
+            .len = VOS_MAC_ADDR_SIZE },
+        [PARAM_DST_MAC_ADDR] = { .type = NLA_BINARY,
+            .len = VOS_MAC_ADDR_SIZE },
+        [PARAM_PERIOD] = { .type = NLA_U32 },
+    };
+
+    ENTER();
+
+    ret = wlan_hdd_validate_context(hdd_ctx);
+    if (0 != ret)
+    {
+        hddLog(LOGE, FL("HDD context is not valid"));
+        return ret;
+    }
+
+    if (!sme_IsFeatureSupportedByFW(WLAN_PERIODIC_TX_PTRN))
+    {
+        hddLog(LOGE,
+            FL("Periodic Tx Pattern Offload feature is not supported in FW!"));
+        return -ENOTSUPP;
+    }
+
+    if (nla_parse(tb, PARAM_MAX, data, data_len, policy))
+    {
+        hddLog(LOGE, FL("Invalid ATTR"));
+        return -EINVAL;
+    }
+
+    if (!tb[PARAM_CONTROL])
+    {
+        hddLog(LOGE, FL("attr control failed"));
+        return -EINVAL;
+    }
+    control = nla_get_u32(tb[PARAM_CONTROL]);
+    hddLog(LOG1, FL("Control: %d"), control);
+
+    if (control == WLAN_START_OFFLOADED_PACKETS)
+        return wlan_hdd_add_tx_ptrn(adapter, hdd_ctx, tb);
+    else if (control == WLAN_STOP_OFFLOADED_PACKETS)
+        return wlan_hdd_del_tx_ptrn(adapter, hdd_ctx, tb);
+    else
+    {
+        hddLog(LOGE, FL("Invalid control: %d"), control);
+        return -EINVAL;
+    }
+}
+
+/*
+ * done with short names for the global vendor params
+ * used by __wlan_hdd_cfg80211_offloaded_packets()
+ */
+#undef PARAM_MAX
+#undef PARAM_REQUEST_ID
+#undef PARAM_CONTROL
+#undef PARAM_IP_PACKET
+#undef PARAM_SRC_MAC_ADDR
+#undef PARAM_DST_MAC_ADDR
+#undef PARAM_PERIOD
+
+/**
+ * wlan_hdd_cfg80211_offloaded_packets() - Wrapper to offload packets
+ * @wiphy:    wiphy structure pointer
+ * @wdev:     Wireless device structure pointer
+ * @data:     Pointer to the data received
+ * @data_len: Length of @data
+ *
+ * Return: 0 on success; errno on failure
+ */
+static int wlan_hdd_cfg80211_offloaded_packets(struct wiphy *wiphy,
+        struct wireless_dev *wdev,
+        const void *data,
+        int data_len)
+{
+    int ret = 0;
+
+    vos_ssr_protect(__func__);
+    ret = __wlan_hdd_cfg80211_offloaded_packets(wiphy,
+            wdev, data, data_len);
+    vos_ssr_unprotect(__func__);
+
+    return ret;
+}
+#endif
+
 const struct wiphy_vendor_command hdd_wiphy_vendor_commands[] =
 {
     {
@@ -6789,7 +7216,17 @@
                  WIPHY_VENDOR_CMD_NEED_NETDEV |
                  WIPHY_VENDOR_CMD_NEED_RUNNING,
         .doit = wlan_hdd_cfg80211_monitor_rssi
+    },
+#ifdef WLAN_FEATURE_OFFLOAD_PACKETS
+    {
+        .info.vendor_id = QCA_NL80211_VENDOR_ID,
+        .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_OFFLOADED_PACKETS,
+        .flags = WIPHY_VENDOR_CMD_NEED_WDEV |
+            WIPHY_VENDOR_CMD_NEED_NETDEV |
+            WIPHY_VENDOR_CMD_NEED_RUNNING,
+        .doit = wlan_hdd_cfg80211_offloaded_packets
     }
+#endif
 };
 
 /* vendor specific events */
diff --git a/CORE/HDD/src/wlan_hdd_main.c b/CORE/HDD/src/wlan_hdd_main.c
index e0a34f5..940814b 100755
--- a/CORE/HDD/src/wlan_hdd_main.c
+++ b/CORE/HDD/src/wlan_hdd_main.c
@@ -393,6 +393,30 @@
 static int curr_con_mode;
 #endif
 
+#ifdef WLAN_FEATURE_OFFLOAD_PACKETS
+/**
+ * hdd_init_offloaded_packets_ctx() - Initialize offload packets context
+ * @hdd_ctx: hdd global context
+ *
+ * Return: none
+ */
+static void hdd_init_offloaded_packets_ctx(hdd_context_t *hdd_ctx)
+{
+    uint8_t i;
+
+    mutex_init(&hdd_ctx->op_ctx.op_lock);
+    for (i = 0; i < MAXNUM_PERIODIC_TX_PTRNS; i++)
+    {
+        hdd_ctx->op_ctx.op_table[i].request_id = 0;
+        hdd_ctx->op_ctx.op_table[i].pattern_id = i;
+    }
+}
+#else
+static void hdd_init_offloaded_packets_ctx(hdd_context_t *hdd_ctx)
+{
+}
+#endif
+
 /**---------------------------------------------------------------------------
 
   \brief hdd_vos_trace_enable() - Configure initial VOS Trace enable
@@ -9674,6 +9698,7 @@
    mutex_init(&pHddCtx->spoofMacAddr.macSpoofingLock);
    mutex_init(&pHddCtx->wmmLock);
 
+   hdd_init_offloaded_packets_ctx(pHddCtx);
    /* By default Strict Regulatory For FCC should be false */
 
    pHddCtx->nEnableStrictRegulatoryForFCC = FALSE;
diff --git a/Kbuild b/Kbuild
index 3c13ce8..a9c8de3 100644
--- a/Kbuild
+++ b/Kbuild
@@ -14,36 +14,39 @@
 endif
 
 ifeq ($(KERNEL_BUILD), 0)
-	# These are configurable via Kconfig for kernel-based builds
-	# Need to explicitly configure for Android-based builds
+# These are configurable via Kconfig for kernel-based builds
+# Need to explicitly configure for Android-based builds
 
-	#Flag to enable BlueTooth AMP feature
-	CONFIG_PRIMA_WLAN_BTAMP := n
+#Flag to enable BlueTooth AMP feature
+    CONFIG_PRIMA_WLAN_BTAMP := n
 
-	#Flag to enable Legacy Fast Roaming(LFR)
-	CONFIG_PRIMA_WLAN_LFR := y
+#Flag to enable Legacy Fast Roaming(LFR)
+    CONFIG_PRIMA_WLAN_LFR := y
 
-	#JB kernel has PMKSA patches, hence enabling this flag
-	CONFIG_PRIMA_WLAN_OKC := y
+#JB kernel has PMKSA patches, hence enabling this flag
+    CONFIG_PRIMA_WLAN_OKC := y
 
-	# JB kernel has CPU enablement patches, so enable
-	CONFIG_PRIMA_WLAN_11AC_HIGH_TP := y
+# JB kernel has CPU enablement patches, so enable
+    CONFIG_PRIMA_WLAN_11AC_HIGH_TP := y
 
-	#Flag to enable TDLS feature
-	CONFIG_QCOM_TDLS := y
+#Flag to enable TDLS feature
+    CONFIG_QCOM_TDLS := y
 
-	#Flag to enable Fast Transition (11r) feature
-	CONFIG_QCOM_VOWIFI_11R := y
+#Flag to enable Fast Transition (11r) feature
+    CONFIG_QCOM_VOWIFI_11R := y
 
-	#Flag to enable Protected Managment Frames (11w) feature
-	ifneq ($(CONFIG_PRONTO_WLAN),)
-	CONFIG_WLAN_FEATURE_11W := y
-	endif
+#Flag to enable Protected Managment Frames (11w) feature
+ifneq ($(CONFIG_PRONTO_WLAN),)
+    CONFIG_WLAN_FEATURE_11W := y
+    endif
 
-	#Flag to enable new Linux Regulatory implementation
-	CONFIG_ENABLE_LINUX_REG := y
+#Flag to enable new Linux Regulatory implementation
+    CONFIG_ENABLE_LINUX_REG := y
 
-endif
+#Flag to enable offload packets feature
+    CONFIG_WLAN_OFFLOAD_PACKETS := y
+
+    endif
 
 # To enable CONFIG_QCOM_ESE_UPLOAD, dependent config
 # CONFIG_QCOM_ESE must be enabled.
@@ -666,6 +669,10 @@
 # Fix build for GCC 4.7
 EXTRA_CFLAGS += -Wno-maybe-uninitialized -Wno-unused-function
 
+ifeq ($(CONFIG_WLAN_OFFLOAD_PACKETS),y)
+CDEFINES += -DWLAN_FEATURE_OFFLOAD_PACKETS
+endif
+
 KBUILD_CPPFLAGS += $(CDEFINES)
 
 # Module information used by KBuild framework
diff --git a/Kconfig b/Kconfig
index 5ed8eb1..9306175 100644
--- a/Kconfig
+++ b/Kconfig
@@ -49,4 +49,8 @@
 	bool "Enable linux regulatory feature"
 	default n
 
+config WLAN_OFFLOAD_PACKETS
+	bool "Enable offload packets feature"
+	default n
+
 endif # PRIMA_WLAN || PRONTO_WLAN