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/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 */