Browse Source

qcacld-3.0: Add support for Android Packet Filter v3

Android Packet Filter 3.0 requires the framework to be able
to read and write into the APF work memory in the Firmware.
It also requires to be able to enable or disable the
interpreter.

Add support for the new read/write/enable/disable operations.

Change-Id: Ic72243b918f4a8385a92b803a1ca3c5305423b52
CRs-Fixed: 2184969
Nachiket Kukade 6 years ago
parent
commit
177b5b06c4

+ 76 - 0
core/hdd/inc/wlan_hdd_apf.h

@@ -31,8 +31,44 @@
 #ifndef __WLAN_HDD_APF_H
 #define __WLAN_HDD_APF_H
 
+#ifdef FEATURE_WLAN_APF
+
+#include <net/cfg80211.h>
 #include "sir_api.h"
 #include "wlan_hdd_main.h"
+#include "wmi_unified_param.h"
+
+#define APF_CONTEXT_MAGIC 0x4575354
+
+#define MAX_APF_MEMORY_LEN	4096
+
+/* APF commands wait times in msec */
+#define WLAN_WAIT_TIME_APF_GET_CAPS     1000
+#define WLAN_WAIT_TIME_APF_READ_MEM     10000
+
+/*
+ * struct hdd_apf_context - hdd Context for apf
+ * @magic: magic number
+ * @qdf_apf_event: Completion variable for APF get operations
+ * @capability_response: capabilities response received from fw
+ * @apf_enabled: True: APF Interpreter enabled, False: Disabled
+ * @cmd_in_progress: Flag that indicates an APF command is in progress
+ * @buf: Buffer to accumulate read memory chunks
+ * @buf_len: Length of the read memory requested
+ * @offset: APF work memory offset to fetch from
+ * @lock: APF Context lock
+ */
+struct hdd_apf_context {
+	unsigned int magic;
+	qdf_event_t qdf_apf_event;
+	struct sir_apf_get_offload capability_response;
+	bool apf_enabled;
+	bool cmd_in_progress;
+	uint8_t *buf;
+	uint32_t buf_len;
+	uint32_t offset;
+	qdf_spinlock_t lock;
+};
 
 /**
  * wlan_hdd_cfg80211_apf_offload() - SSR Wrapper to APF Offload
@@ -47,4 +83,44 @@
 int wlan_hdd_cfg80211_apf_offload(struct wiphy *wiphy,
 				  struct wireless_dev *wdev,
 				  const void *data, int data_len);
+
+/**
+ * hdd_apf_context_init - APF Context initialization operations
+ *
+ * Return: None
+ */
+void hdd_apf_context_init(void);
+
+/**
+ * hdd_apf_context_destroy - APF Context de-init operations
+ *
+ * Return: None
+ */
+void hdd_apf_context_destroy(void);
+
+/**
+ * hdd_get_apf_capabilities_cb() - Callback function to get APF capabilities
+ * @hdd_context: hdd_context
+ * @apf_get_offload: struct for get offload
+ *
+ * This function receives the response/data from the lower layer and
+ * checks to see if the thread is still waiting then post the results to
+ * upper layer, if the request has timed out then ignore.
+ *
+ * Return: None
+ */
+void hdd_get_apf_capabilities_cb(void *hdd_context,
+				 struct sir_apf_get_offload *data);
+#else /* FEATURE_WLAN_APF */
+
+static inline void hdd_apf_context_init(void)
+{
+}
+
+static inline void hdd_apf_context_destroy(void)
+{
+}
+
+#endif /* FEATURE_WLAN_APF */
+
 #endif /* WLAN_HDD_APF_H */

+ 384 - 26
core/hdd/src/wlan_hdd_apf.c

@@ -26,13 +26,15 @@
 #include "qca_vendor.h"
 #include "wlan_hdd_request_manager.h"
 
+struct hdd_apf_context apf_context;
+
 /*
  * define short names for the global vendor params
  * used by __wlan_hdd_cfg80211_apf_offload()
  */
 #define APF_INVALID \
 	QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_INVALID
-#define APF_SET_RESET \
+#define APF_SUBCMD \
 	QCA_WLAN_VENDOR_ATTR_SET_RESET_PACKET_FILTER
 #define APF_VERSION \
 	QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_VERSION
@@ -44,25 +46,43 @@
 	QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_CURRENT_OFFSET
 #define APF_PROGRAM \
 	QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_PROGRAM
+#define APF_PROG_LEN \
+	QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_PROG_LENGTH
 #define APF_MAX \
 	QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_MAX
 
 static const struct nla_policy
 wlan_hdd_apf_offload_policy[APF_MAX + 1] = {
-	[APF_SET_RESET] = {.type = NLA_U32},
+	[APF_SUBCMD] = {.type = NLA_U32},
 	[APF_VERSION] = {.type = NLA_U32},
 	[APF_FILTER_ID] = {.type = NLA_U32},
 	[APF_PACKET_SIZE] = {.type = NLA_U32},
 	[APF_CURRENT_OFFSET] = {.type = NLA_U32},
-	[APF_PROGRAM] = {.type = NLA_U8},
+	[APF_PROGRAM] = {.type = NLA_BINARY,
+			 .len = MAX_APF_MEMORY_LEN},
+	[APF_PROG_LEN] = {.type = NLA_U32},
 };
 
+void hdd_apf_context_init(void)
+{
+	qdf_event_create(&apf_context.qdf_apf_event);
+	qdf_spinlock_create(&apf_context.lock);
+	apf_context.apf_enabled = true;
+}
+
+void hdd_apf_context_destroy(void)
+{
+	qdf_event_destroy(&apf_context.qdf_apf_event);
+	qdf_spinlock_destroy(&apf_context.lock);
+	qdf_mem_zero(&apf_context, sizeof(apf_context));
+}
+
 struct apf_offload_priv {
 	struct sir_apf_get_offload apf_get_offload;
 };
 
-static void hdd_get_apf_offload_cb(void *context,
-				   struct sir_apf_get_offload *data)
+void hdd_get_apf_capabilities_cb(void *context,
+				 struct sir_apf_get_offload *data)
 {
 	struct hdd_request *request;
 	struct apf_offload_priv *priv;
@@ -129,12 +149,12 @@ nla_put_failure:
 }
 
 /**
- * hdd_get_apf_offload - Get APF offload Capabilities
+ * hdd_get_apf_capabilities - Get APF offload Capabilities
  * @hdd_ctx: Hdd context
  *
  * Return: 0 on success, errno on failure
  */
-static int hdd_get_apf_offload(struct hdd_context *hdd_ctx)
+static int hdd_get_apf_capabilities(struct hdd_context *hdd_ctx)
 {
 	QDF_STATUS status;
 	int ret;
@@ -155,9 +175,9 @@ static int hdd_get_apf_offload(struct hdd_context *hdd_ctx)
 	}
 	cookie = hdd_request_cookie(request);
 
-	status = sme_get_apf_offload_capabilities(hdd_ctx->hHal,
-						  hdd_get_apf_offload_cb,
-						  cookie);
+	status = sme_get_apf_capabilities(hdd_ctx->hHal,
+					  hdd_get_apf_capabilities_cb,
+					  cookie);
 	if (!QDF_IS_STATUS_SUCCESS(status)) {
 		hdd_err("Unable to retrieve APF caps");
 		ret = qdf_status_to_os_return(status);
@@ -206,13 +226,10 @@ static int hdd_set_reset_apf_offload(struct hdd_context *hdd_ctx,
 
 	hdd_enter();
 
-	if (adapter->device_mode == QDF_STA_MODE ||
-	    adapter->device_mode == QDF_P2P_CLIENT_MODE) {
-		if (!hdd_conn_is_connected(
-		    WLAN_HDD_GET_STATION_CTX_PTR(adapter))) {
-			hdd_err("Not in Connected state!");
-			return -ENOTSUPP;
-		}
+	if (!hdd_conn_is_connected(
+	    WLAN_HDD_GET_STATION_CTX_PTR(adapter))) {
+		hdd_err("Not in Connected state!");
+		return -ENOTSUPP;
 	}
 
 	apf_set_offload = qdf_mem_malloc(sizeof(*apf_set_offload));
@@ -296,6 +313,299 @@ fail:
 	return ret;
 }
 
+/**
+ * hdd_enable_disable_apf - Enable or Disable the APF interpreter
+ * @vdev_id: VDEV id
+ * @hdd_ctx: Hdd context
+ * @apf_enable: true: Enable APF Int., false: disable APF Int.
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int
+hdd_enable_disable_apf(struct hdd_context *hdd_ctx, uint8_t vdev_id,
+		       bool apf_enable)
+{
+	QDF_STATUS status;
+
+	hdd_enter();
+
+	status = sme_set_apf_enable_disable(hdd_ctx->hHal, vdev_id, apf_enable);
+	if (!QDF_IS_STATUS_SUCCESS(status)) {
+		hdd_err("Unable to post sme apf enable/disable message (status-%d)",
+				status);
+		return -EINVAL;
+	}
+
+	qdf_spin_lock(&apf_context.lock);
+	apf_context.apf_enabled = apf_enable;
+	qdf_spin_unlock(&apf_context.lock);
+
+	hdd_exit();
+	return 0;
+}
+
+/**
+ * hdd_apf_write_memory - Write into the apf work memory
+ * @hdd_ctx: Hdd context
+ * @tb: list of attributes
+ * @session_id: Session id
+ *
+ * This function writes code/data into the APF work memory and
+ * provides program length that is passed on to the interpreter.
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int
+hdd_apf_write_memory(struct hdd_context *hdd_ctx, struct nlattr **tb,
+		     uint8_t session_id)
+{
+	struct wmi_apf_write_memory_params write_mem_params = {0};
+	QDF_STATUS status;
+	int ret = 0;
+	bool apf_enabled;
+
+	hdd_enter();
+
+	write_mem_params.vdev_id = session_id;
+
+	qdf_spin_lock(&apf_context.lock);
+	apf_enabled = apf_context.apf_enabled;
+	qdf_spin_unlock(&apf_context.lock);
+
+	if (apf_enabled) {
+		hdd_err("Cannot get/set when APF interpreter is enabled");
+		return -EINVAL;
+	}
+
+	/* Read program length */
+	if (!tb[APF_PROG_LEN]) {
+		hdd_err("attr program length failed");
+		return -EINVAL;
+	}
+	write_mem_params.program_len = nla_get_u32(tb[APF_PROG_LEN]);
+
+	/* Read APF work memory offset */
+	if (!tb[APF_CURRENT_OFFSET]) {
+		hdd_err("attr apf packet size failed");
+		return -EINVAL;
+	}
+	write_mem_params.addr_offset = nla_get_u32(tb[APF_CURRENT_OFFSET]);
+
+	/* Parse and fetch apf program */
+	if (!tb[APF_PROGRAM]) {
+		hdd_err("attr apf program failed");
+		return -EINVAL;
+	}
+
+	write_mem_params.length = nla_len(tb[APF_PROGRAM]);
+	if (!write_mem_params.length) {
+		hdd_err("Program attr with empty data");
+		return -EINVAL;
+	}
+
+	write_mem_params.buf = qdf_mem_malloc(sizeof(uint8_t)
+						* write_mem_params.length);
+	if (write_mem_params.buf == NULL) {
+		hdd_err("failed to alloc mem for apf write mem operation");
+		return -EINVAL;
+	}
+	nla_memcpy(write_mem_params.buf, tb[APF_PROGRAM],
+		   write_mem_params.length);
+
+	write_mem_params.apf_version =
+				apf_context.capability_response.apf_version;
+
+	status = sme_apf_write_work_memory(hdd_ctx->hHal, &write_mem_params);
+	if (!QDF_IS_STATUS_SUCCESS(status)) {
+		hdd_err("Unable to retrieve APF caps");
+		ret = -EINVAL;
+	}
+
+	if (write_mem_params.buf)
+		qdf_mem_free(write_mem_params.buf);
+
+	hdd_exit();
+	return ret;
+}
+
+/**
+ * hdd_apf_read_memory_callback - HDD Callback for the APF read memory
+ *	operation
+ * @context: Hdd context
+ * @read_mem_evt: APF read memory event response parameters
+ *
+ * Return: 0 on success, errno on failure
+ */
+static void
+hdd_apf_read_memory_callback(void *hdd_context,
+			     struct wmi_apf_read_memory_resp_event_params *evt)
+{
+	struct hdd_context *hdd_ctx = hdd_context;
+	static struct hdd_apf_context *context = &apf_context;
+	uint8_t *buf_ptr;
+	uint32_t pkt_offset;
+
+	hdd_enter();
+
+	if (wlan_hdd_validate_context(hdd_ctx) || !evt) {
+		hdd_err("HDD context is invalid or event buf(%pK) is null",
+			evt);
+		return;
+	}
+
+	qdf_spin_lock(&context->lock);
+	if (context->magic != APF_CONTEXT_MAGIC) {
+		/* The caller presumably timed out, nothing to do */
+		qdf_spin_unlock(&context->lock);
+		hdd_err("Caller timed out or corrupt magic, simply return");
+		return;
+	}
+
+	if (evt->offset <  context->offset) {
+		qdf_spin_unlock(&context->lock);
+		hdd_err("Offset in read event(%d) smaller than offset in request(%d)!",
+					evt->offset, context->offset);
+		return;
+	}
+
+	/*
+	 * offset in the event is relative to the APF work memory.
+	 * Calculate the packet offset, which gives us the relative
+	 * location in the buffer to start copy into.
+	 */
+	pkt_offset = evt->offset - context->offset;
+
+	if (context->buf_len < pkt_offset + evt->length) {
+		qdf_spin_unlock(&context->lock);
+		hdd_err("Read chunk exceeding allocated space");
+		return;
+	}
+	buf_ptr = context->buf + pkt_offset;
+
+	qdf_mem_copy(buf_ptr, evt->data, evt->length);
+
+	if (!evt->more_data) {
+		/* Release the caller after last event, clear magic */
+		context->magic = 0;
+		qdf_event_set(&context->qdf_apf_event);
+	}
+
+	qdf_spin_unlock(&context->lock);
+
+	hdd_exit();
+}
+
+/**
+ * hdd_apf_read_memory - Read part of the apf work memory
+ * @hdd_ctx: Hdd context
+ * @tb: list of attributes
+ * @session_id: Session id
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int hdd_apf_read_memory(struct hdd_context *hdd_ctx, struct nlattr **tb,
+			       uint8_t session_id)
+{
+	struct wmi_apf_read_memory_params read_mem_params = {0};
+	static struct hdd_apf_context *context = &apf_context;
+	QDF_STATUS status;
+	unsigned long nl_buf_len = NLMSG_HDRLEN;
+	int ret = 0;
+	struct sk_buff *skb = NULL;
+	uint8_t *bufptr;
+
+	hdd_enter();
+
+	read_mem_params.vdev_id = session_id;
+
+	/* Read APF work memory offset */
+	if (!tb[APF_CURRENT_OFFSET]) {
+		hdd_err("attr apf memory offset failed");
+		return -EINVAL;
+	}
+	read_mem_params.addr_offset = nla_get_u32(tb[APF_CURRENT_OFFSET]);
+
+	/* Read length */
+	if (!tb[APF_PACKET_SIZE]) {
+		hdd_err("attr apf packet size failed");
+		return -EINVAL;
+	}
+	read_mem_params.length = nla_get_u32(tb[APF_PACKET_SIZE]);
+	if (!read_mem_params.length) {
+		hdd_err("apf read length cannot be zero!");
+		return -EINVAL;
+	}
+	bufptr = qdf_mem_malloc(read_mem_params.length);
+	if (bufptr == NULL) {
+		hdd_err("alloc failed for cumulative event buffer");
+		return -ENOMEM;
+	}
+
+	qdf_spin_lock(&context->lock);
+	if (context->apf_enabled) {
+		qdf_spin_unlock(&context->lock);
+		hdd_err("Cannot get/set while interpreter is enabled");
+		return -EINVAL;
+	}
+
+	qdf_event_reset(&context->qdf_apf_event);
+	context->offset = read_mem_params.addr_offset;
+
+	context->buf = bufptr;
+	context->buf_len = read_mem_params.length;
+	context->magic = APF_CONTEXT_MAGIC;
+	qdf_spin_unlock(&context->lock);
+
+	status = sme_apf_read_work_memory(hdd_ctx->hHal, &read_mem_params,
+					  hdd_apf_read_memory_callback);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		hdd_err("Unable to post sme APF read memory message (status-%d)",
+				status);
+		ret = -EINVAL;
+		goto fail;
+	}
+
+	/* request was sent -- wait for the response */
+	status = qdf_wait_for_event_completion(&context->qdf_apf_event,
+					       WLAN_WAIT_TIME_APF_READ_MEM);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		hdd_err("Target response timed out");
+		qdf_spin_lock(&context->lock);
+		context->magic = 0;
+		qdf_spin_unlock(&context->lock);
+		ret = -ETIMEDOUT;
+		goto fail;
+	}
+
+	nl_buf_len += sizeof(uint32_t) + NLA_HDRLEN;
+	nl_buf_len += context->buf_len + NLA_HDRLEN;
+
+	skb = cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy, nl_buf_len);
+	if (!skb) {
+		hdd_err("cfg80211_vendor_cmd_alloc_reply_skb failed");
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	if (nla_put_u32(skb, APF_SUBCMD, QCA_WLAN_READ_PACKET_FILTER) ||
+	    nla_put(skb, APF_PROGRAM, read_mem_params.length, context->buf)) {
+		hdd_err("put fail");
+		kfree_skb(skb);
+		ret = -EINVAL;
+		goto fail;
+	}
+
+	cfg80211_vendor_cmd_reply(skb);
+fail:
+	if (context->buf) {
+		qdf_mem_free(context->buf);
+		context->buf = NULL;
+	}
+
+	hdd_exit();
+	return ret;
+}
+
 /**
  * wlan_hdd_cfg80211_apf_offload() - Set/Reset to APF Offload
  * @wiphy:    wiphy structure pointer
@@ -314,7 +624,9 @@ __wlan_hdd_cfg80211_apf_offload(struct wiphy *wiphy,
 	struct net_device *dev = wdev->netdev;
 	struct hdd_adapter *adapter =  WLAN_HDD_GET_PRIV_PTR(dev);
 	struct nlattr *tb[APF_MAX + 1];
-	int ret_val, packet_filter_subcmd;
+	int ret_val = 0, apf_subcmd;
+	uint8_t session_id = adapter->session_id;
+	static struct hdd_apf_context *context = &apf_context;
 
 	hdd_enter();
 
@@ -338,18 +650,64 @@ __wlan_hdd_cfg80211_apf_offload(struct wiphy *wiphy,
 		return -EINVAL;
 	}
 
-	if (!tb[APF_SET_RESET]) {
-		hdd_err("attr apf set reset failed");
+	if (!tb[APF_SUBCMD]) {
+		hdd_err("attr apf sub-command failed");
 		return -EINVAL;
 	}
+	apf_subcmd = nla_get_u32(tb[APF_SUBCMD]);
+
+	if (!(adapter->device_mode == QDF_STA_MODE ||
+	      adapter->device_mode == QDF_P2P_CLIENT_MODE)) {
+			hdd_err("APF only supported in STA or P2P CLI modes!");
+			return -ENOTSUPP;
+	}
+
+	qdf_spin_lock(&context->lock);
+	if (context->cmd_in_progress) {
+		qdf_spin_unlock(&context->lock);
+		hdd_err("Another APF cmd in progress, try again later!");
+		return -EAGAIN;
+	}
+	context->cmd_in_progress = true;
+	qdf_spin_unlock(&context->lock);
+
+	switch (apf_subcmd) {
+	/* Legacy APF sub-commands */
+	case QCA_WLAN_SET_PACKET_FILTER:
+		ret_val = hdd_set_reset_apf_offload(hdd_ctx, tb,
+						    adapter);
+		break;
+	case QCA_WLAN_GET_PACKET_FILTER:
+		ret_val = hdd_get_apf_capabilities(hdd_ctx);
+		break;
+
+	/* APF 3.0 sub-commands */
+	case QCA_WLAN_WRITE_PACKET_FILTER:
+		ret_val = hdd_apf_write_memory(hdd_ctx, tb, session_id);
+		break;
+	case QCA_WLAN_READ_PACKET_FILTER:
+		ret_val = hdd_apf_read_memory(hdd_ctx, tb, session_id);
+		break;
+	case QCA_WLAN_ENABLE_PACKET_FILTER:
+		ret_val = hdd_enable_disable_apf(hdd_ctx,
+						 session_id,
+						 true);
+		break;
+	case QCA_WLAN_DISABLE_PACKET_FILTER:
+		ret_val = hdd_enable_disable_apf(hdd_ctx,
+						 session_id,
+						 false);
+		break;
+	default:
+		hdd_err("Unknown APF Sub-command: %d", apf_subcmd);
+		ret_val = -ENOTSUPP;
+	}
 
-	packet_filter_subcmd = nla_get_u32(tb[APF_SET_RESET]);
+	qdf_spin_lock(&context->lock);
+	context->cmd_in_progress = false;
+	qdf_spin_unlock(&context->lock);
 
-	if (packet_filter_subcmd == QCA_WLAN_GET_PACKET_FILTER)
-		return hdd_get_apf_offload(hdd_ctx);
-	else
-		return hdd_set_reset_apf_offload(hdd_ctx, tb,
-						 adapter);
+	return ret_val;
 }
 
 int

+ 2 - 0
core/hdd/src/wlan_hdd_cfg80211.c

@@ -92,7 +92,9 @@
 #include "wlan_hdd_disa.h"
 #include "wlan_hdd_request_manager.h"
 #include "wlan_hdd_he.h"
+#ifdef FEATURE_WLAN_APF
 #include "wlan_hdd_apf.h"
+#endif
 
 #include <cdp_txrx_cmn.h>
 #include <cdp_txrx_misc.h>

+ 4 - 0
core/hdd/src/wlan_hdd_main.c

@@ -6754,6 +6754,8 @@ static int hdd_context_deinit(struct hdd_context *hdd_ctx)
 
 	qdf_list_destroy(&hdd_ctx->hdd_adapters);
 
+	hdd_apf_context_destroy();
+
 	return 0;
 }
 
@@ -8644,6 +8646,8 @@ static int hdd_context_init(struct hdd_context *hdd_ctx)
 	init_completion(&hdd_ctx->mc_sus_event_var);
 	init_completion(&hdd_ctx->ready_to_suspend);
 
+	hdd_apf_context_init();
+
 	qdf_spinlock_create(&hdd_ctx->connection_status_lock);
 	qdf_spinlock_create(&hdd_ctx->sta_update_info_lock);
 	qdf_spinlock_create(&hdd_ctx->hdd_adapter_lock);

+ 60 - 5
core/sme/inc/sme_api.h

@@ -1297,8 +1297,9 @@ QDF_STATUS sme_add_beacon_filter(tHalHandle hal,
 				uint32_t session_id, uint32_t *ie_map);
 QDF_STATUS sme_remove_beacon_filter(tHalHandle hal, uint32_t session_id);
 
+#ifdef FEATURE_WLAN_APF
 /**
- * sme_get_apf_offload_capabilities() - Get APF offload capabilities
+ * sme_get_apf_capabilities() - Get APF capabilities
  * @hal: Global HAL handle
  * @callback: Callback function to be called with the result
  * @context: Opaque context to be used by the caller to associate the
@@ -1309,12 +1310,66 @@ QDF_STATUS sme_remove_beacon_filter(tHalHandle hal, uint32_t session_id);
  *
  * Return: QDF_STATUS enumeration
  */
-QDF_STATUS sme_get_apf_offload_capabilities(tHalHandle hal,
-					    apf_get_offload_cb callback,
-					    void *context);
+QDF_STATUS sme_get_apf_capabilities(tHalHandle hal,
+				    apf_get_offload_cb callback,
+				    void *context);
 
+/**
+ * sme_set_apf_instructions() - Set APF apf filter instructions.
+ * @hal: HAL handle
+ * @apf_set_offload: struct to set apf filter instructions.
+ *
+ * APFv2 (Legacy APF) API to set the APF packet filter.
+ *
+ * Return: QDF_STATUS enumeration.
+ */
 QDF_STATUS sme_set_apf_instructions(tHalHandle hal,
-				    struct sir_apf_set_offload *);
+				    struct sir_apf_set_offload
+							*apf_set_offload);
+
+/**
+ * sme_set_apf_enable_disable - Send apf enable/disable cmd
+ * @hal: global hal handle
+ * @vdev_id: vdev id
+ * @apf_enable: true: Enable APF Int., false: Disable APF Int.
+ *
+ * API to either enable or disable the APF interpreter.
+ *
+ * Return: QDF_STATUS enumeration.
+ */
+QDF_STATUS sme_set_apf_enable_disable(tHalHandle hal, uint8_t vdev_id,
+				      bool apf_enable);
+
+/**
+ * sme_apf_write_work_memory - Write into the apf work memory
+ * @hal: global hal handle
+ * @write_params: APF parameters for the write operation
+ *
+ * API for writing into the APF work memory.
+ *
+ * Return: QDF_STATUS enumeration.
+ */
+QDF_STATUS sme_apf_write_work_memory(tHalHandle hal,
+				    struct wmi_apf_write_memory_params
+								*write_params);
+
+/**
+ * sme_apf_read_work_memory - Read part of apf work memory
+ * @hal: global hal handle
+ * @read_params: APF parameters for the get operation
+ * @callback: callback to handle the the read response
+ *
+ * API for issuing a APF read memory request.
+ *
+ * Return: QDF_STATUS enumeration.
+ */
+QDF_STATUS
+sme_apf_read_work_memory(tHalHandle hal,
+			 struct wmi_apf_read_memory_params *read_params,
+			 apf_read_mem_cb callback);
+
+#endif /* FEATURE_WLAN_APF */
+
 uint32_t sme_get_wni_dot11_mode(tHalHandle hal);
 QDF_STATUS sme_create_mon_session(tHalHandle hal_handle, uint8_t *bssid);
 QDF_STATUS sme_set_adaptive_dwelltime_config(tHalHandle hal,

+ 16 - 1
core/sme/inc/sme_internal.h

@@ -123,6 +123,7 @@ typedef void (*p2p_lo_callback)(void *context, void *event);
 typedef void (*sme_send_oem_data_rsp_msg)(struct oem_data_rsp *);
 #endif
 
+#ifdef FEATURE_WLAN_APF
 /**
  * typedef apf_get_offload_cb - APF offload callback signature
  * @context: Opaque context that the client can use to associate the
@@ -133,6 +134,17 @@ struct sir_apf_get_offload;
 typedef void (*apf_get_offload_cb)(void *context,
 				   struct sir_apf_get_offload *caps);
 
+/**
+ * typedef apf_read_mem_cb - APF read memory response callback
+ * @context: Opaque context that the client can use to associate the
+ *    callback with the request
+ * @evt: APF read memory response event parameters
+ */
+typedef void (*apf_read_mem_cb)(void *context,
+				struct wmi_apf_read_memory_resp_event_params
+									  *evt);
+#endif /* FEATURE_WLAN_APF */
+
 /**
  * typedef sme_encrypt_decrypt_callback - encrypt/decrypt callback
  *    signature
@@ -228,7 +240,6 @@ typedef struct tagSmeStruct {
 	ocb_callback dcc_stats_event_callback;
 	sme_set_thermal_level_callback set_thermal_level_cb;
 	void *apf_get_offload_context;
-	apf_get_offload_cb apf_get_offload_cb;
 	p2p_lo_callback p2p_lo_event_callback;
 	void *p2p_lo_event_context;
 #ifdef FEATURE_OEM_DATA_SUPPORT
@@ -259,6 +270,10 @@ typedef struct tagSmeStruct {
 	void (*twt_enable_cb)(void *hdd_ctx,
 			struct wmi_twt_enable_complete_event_param *params);
 	void (*twt_disable_cb)(void *hdd_ctx);
+#ifdef FEATURE_WLAN_APF
+	apf_get_offload_cb apf_get_offload_cb;
+	apf_read_mem_cb apf_read_mem_cb;
+#endif
 } tSmeStruct, *tpSmeStruct;
 
 #endif /* #if !defined( __SMEINTERNAL_H ) */

+ 64 - 11
core/sme/src/common/sme_api.c

@@ -14115,9 +14115,10 @@ void sme_send_disassoc_req_frame(tHalHandle hal, uint8_t session_id,
 			FL("cds_send_mb_message Failed"));
 }
 
-QDF_STATUS sme_get_apf_offload_capabilities(tHalHandle hal,
-					    apf_get_offload_cb callback,
-					    void *context)
+#ifdef FEATURE_WLAN_APF
+QDF_STATUS sme_get_apf_capabilities(tHalHandle hal,
+				    apf_get_offload_cb callback,
+				    void *context)
 {
 	QDF_STATUS          status     = QDF_STATUS_SUCCESS;
 	tpAniSirGlobal      mac_ctx      = PMAC_STRUCT(hal);
@@ -14147,14 +14148,6 @@ QDF_STATUS sme_get_apf_offload_capabilities(tHalHandle hal,
 	return status;
 }
 
-
-/**
- * sme_set_apf_instructions() - Set APF apf filter instructions.
- * @hal: HAL handle
- * @apf_set_offload: struct to set apf filter instructions.
- *
- * Return: QDF_STATUS enumeration.
- */
 QDF_STATUS sme_set_apf_instructions(tHalHandle hal,
 				    struct sir_apf_set_offload *req)
 {
@@ -14205,6 +14198,66 @@ QDF_STATUS sme_set_apf_instructions(tHalHandle hal,
 	return status;
 }
 
+QDF_STATUS sme_set_apf_enable_disable(tHalHandle hal, uint8_t vdev_id,
+				      bool apf_enable)
+{
+	void *wma_handle;
+
+	wma_handle = cds_get_context(QDF_MODULE_ID_WMA);
+	if (!wma_handle) {
+		QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR,
+				"wma handle is NULL");
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	return wma_send_apf_enable_cmd(wma_handle, vdev_id, apf_enable);
+}
+
+QDF_STATUS
+sme_apf_write_work_memory(tHalHandle hal,
+			struct wmi_apf_write_memory_params *write_params)
+{
+	void *wma_handle;
+
+	wma_handle = cds_get_context(QDF_MODULE_ID_WMA);
+	if (!wma_handle) {
+		QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR,
+				"wma handle is NULL");
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	return wma_send_apf_write_work_memory_cmd(wma_handle, write_params);
+}
+
+QDF_STATUS
+sme_apf_read_work_memory(tHalHandle hal,
+			 struct wmi_apf_read_memory_params *read_params,
+			 apf_read_mem_cb callback)
+{
+	QDF_STATUS status   = QDF_STATUS_SUCCESS;
+	tpAniSirGlobal mac  = PMAC_STRUCT(hal);
+	void *wma_handle;
+
+	status = sme_acquire_global_lock(&mac->sme);
+	if (QDF_IS_STATUS_SUCCESS(status)) {
+		mac->sme.apf_read_mem_cb = callback;
+		sme_release_global_lock(&mac->sme);
+	} else {
+		QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR,
+			FL("sme_acquire_global_lock failed"));
+	}
+
+	wma_handle = cds_get_context(QDF_MODULE_ID_WMA);
+	if (!wma_handle) {
+		QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR,
+				"wma handle is NULL");
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	return wma_send_apf_read_work_memory_cmd(wma_handle, read_params);
+}
+#endif /* FEATURE_WLAN_APF */
+
 /**
  * sme_get_wni_dot11_mode() - return configured wni dot11mode
  * @hal: hal pointer

+ 90 - 1
core/wma/inc/wma.h

@@ -2148,14 +2148,103 @@ void wma_process_fw_test_cmd(WMA_HANDLE handle,
 QDF_STATUS wma_send_ht40_obss_scanind(tp_wma_handle wma,
 	struct obss_ht40_scanind *req);
 
+uint32_t wma_get_num_of_setbits_from_bitmask(uint32_t mask);
+
+#ifdef FEATURE_WLAN_APF
+/**
+ *  wma_get_apf_caps_event_handler() - Event handler for get apf capability
+ *  @handle: WMA global handle
+ *  @cmd_param_info: command event data
+ *  @len: Length of @cmd_param_info
+ *
+ *  Return: 0 on Success or Errno on failure
+ */
 int wma_get_apf_caps_event_handler(void *handle,
 				   u_int8_t *cmd_param_info,
 				   u_int32_t len);
-uint32_t wma_get_num_of_setbits_from_bitmask(uint32_t mask);
+
+/**
+ * wma_get_apf_capabilities - Send get apf capability to firmware
+ * @wma_handle: wma handle
+ *
+ * Return: QDF_STATUS enumeration.
+ */
 QDF_STATUS wma_get_apf_capabilities(tp_wma_handle wma);
+
+/**
+ *  wma_set_apf_instructions - Set apf instructions to firmware
+ *  @wma: wma handle
+ *  @apf_set_offload: APF offload information to set to firmware
+ *
+ *  Return: QDF_STATUS enumeration
+ */
 QDF_STATUS
 wma_set_apf_instructions(tp_wma_handle wma,
 			 struct sir_apf_set_offload *apf_set_offload);
+
+/**
+ * wma_send_apf_enable_cmd - Send apf enable/disable cmd
+ * @wma_handle: wma handle
+ * @vdev_id: vdev id
+ * @apf_enable: true: Enable APF Int., false: Disable APF Int.
+ *
+ * Return: QDF_STATUS enumeration.
+ */
+QDF_STATUS wma_send_apf_enable_cmd(WMA_HANDLE handle, uint8_t vdev_id,
+				   bool apf_enable);
+
+/**
+ * wma_send_apf_write_work_memory_cmd - Command to write into the apf work
+ * memory
+ * @wma_handle: wma handle
+ * @write_params: APF parameters for the write operation
+ *
+ * Return: QDF_STATUS enumeration.
+ */
+QDF_STATUS
+wma_send_apf_write_work_memory_cmd(WMA_HANDLE handle,
+				   struct wmi_apf_write_memory_params
+								*write_params);
+
+/**
+ * wma_send_apf_read_work_memory_cmd - Command to get part of apf work memory
+ * @wma_handle: wma handle
+ * @callback: HDD callback to receive apf get mem event
+ * @context: Context for the HDD callback
+ * @read_params: APF parameters for the get operation
+ *
+ * Return: QDF_STATUS enumeration.
+ */
+QDF_STATUS
+wma_send_apf_read_work_memory_cmd(WMA_HANDLE handle,
+				  struct wmi_apf_read_memory_params
+								*read_params);
+
+/**
+ * wma_apf_read_work_memory_event_handler - Event handler for get apf mem
+ * operation
+ * @handle: wma handle
+ * @evt_buf: Buffer pointer to the event
+ * @len: Length of the event buffer
+ *
+ * Return: status.
+ */
+int wma_apf_read_work_memory_event_handler(void *handle, uint8_t *evt_buf,
+					   uint32_t len);
+#else /* FEATURE_WLAN_APF */
+static inline QDF_STATUS wma_get_apf_capabilities(tp_wma_handle wma)
+{
+	return QDF_STATUS_SUCCESS;
+}
+
+static inline QDF_STATUS
+wma_set_apf_instructions(tp_wma_handle wma,
+			 struct sir_apf_set_offload *apf_set_offload)
+{
+	return QDF_STATUS_SUCCESS;
+}
+#endif /* FEATURE_WLAN_APF */
+
 void wma_process_set_pdev_ie_req(tp_wma_handle wma,
 		struct set_ie_param *ie_params);
 void wma_process_set_pdev_ht_ie_req(tp_wma_handle wma,

+ 7 - 0
core/wma/src/wma_dev_if.c

@@ -1807,6 +1807,7 @@ static int wma_remove_bss_peer(tp_wma_handle wma, void *pdev,
 	return ret_value;
 }
 
+#ifdef FEATURE_WLAN_APF
 /*
  * get_fw_active_apf_mode() - convert HDD APF mode to FW configurable APF
  * mode
@@ -1850,6 +1851,12 @@ static QDF_STATUS wma_config_active_apf_mode(t_wma_handle *wma, uint8_t vdev_id)
 	return wmi_unified_set_active_apf_mode_cmd(wma->wmi_handle, vdev_id,
 						   uc_mode, mcbc_mode);
 }
+#else /* FEATURE_WLAN_APF */
+static QDF_STATUS wma_config_active_apf_mode(t_wma_handle *wma, uint8_t vdev_id)
+{
+	return QDF_STATUS_SUCCESS;
+}
+#endif /* FEATURE_WLAN_APF */
 
 #ifdef FEATURE_AP_MCC_CH_AVOIDANCE
 /**

+ 137 - 14
core/wma/src/wma_features.c

@@ -1635,7 +1635,7 @@ static const u8 *wma_wow_wake_reason_str(A_INT32 wake_reason)
 	case WOW_REASON_ACTION_FRAME_RECV:
 		return "ACTION_FRAME_RECV";
 	case WOW_REASON_BPF_ALLOW:
-		return "APF_ALLOW";
+		return "BPF_ALLOW";
 	case WOW_REASON_NAN_DATA:
 		return "NAN_DATA";
 	case WOW_REASON_OEM_RESPONSE_EVENT:
@@ -4381,6 +4381,7 @@ QDF_STATUS wma_process_set_ie_info(tp_wma_handle wma,
 	return ret;
 }
 
+#ifdef FEATURE_WLAN_APF
 /**
  *  wma_get_apf_caps_event_handler() - Event handler for get apf capability
  *  @handle: WMA global handle
@@ -4431,12 +4432,6 @@ int wma_get_apf_caps_event_handler(void *handle, u_int8_t *cmd_param_info,
 	return 0;
 }
 
-/**
- * wma_get_apf_capabilities - Send get apf capability to firmware
- * @wma_handle: wma handle
- *
- * Return: QDF_STATUS enumeration.
- */
 QDF_STATUS wma_get_apf_capabilities(tp_wma_handle wma)
 {
 	QDF_STATUS status = QDF_STATUS_SUCCESS;
@@ -4478,13 +4473,6 @@ QDF_STATUS wma_get_apf_capabilities(tp_wma_handle wma)
 	return status;
 }
 
-/**
- *  wma_set_apf_instructions - Set apf instructions to firmware
- *  @wma: wma handle
- *  @apf_set_offload: apf offload information to set to firmware
- *
- *  Return: QDF_STATUS enumeration
- */
 QDF_STATUS wma_set_apf_instructions(tp_wma_handle wma,
 				    struct sir_apf_set_offload *apf_set_offload)
 {
@@ -4568,6 +4556,141 @@ QDF_STATUS wma_set_apf_instructions(tp_wma_handle wma,
 	return QDF_STATUS_SUCCESS;
 }
 
+QDF_STATUS wma_send_apf_enable_cmd(WMA_HANDLE handle, uint8_t vdev_id,
+				   bool apf_enable)
+{
+	QDF_STATUS status = QDF_STATUS_SUCCESS;
+	tp_wma_handle wma = (tp_wma_handle) handle;
+
+	if (!wma || !wma->wmi_handle) {
+		WMA_LOGE(FL("WMA is closed, can not issue get APF capab"));
+		return QDF_STATUS_E_INVAL;
+	}
+
+	if (!WMI_SERVICE_IS_ENABLED(wma->wmi_service_bitmap,
+		WMI_SERVICE_BPF_OFFLOAD)) {
+		WMA_LOGE(FL("APF cababilities feature bit not enabled"));
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	status = wmi_unified_send_apf_enable_cmd(wma->wmi_handle, vdev_id,
+						 apf_enable);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		WMA_LOGE("Failed to send apf enable/disable cmd");
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	if (apf_enable)
+		WMA_LOGD("Sent APF Enable on vdevid: %d", vdev_id);
+	else
+		WMA_LOGD("Sent APF Disable on vdevid: %d", vdev_id);
+
+	return status;
+}
+
+QDF_STATUS
+wma_send_apf_write_work_memory_cmd(WMA_HANDLE handle,
+				   struct wmi_apf_write_memory_params
+								*write_params)
+{
+	QDF_STATUS status = QDF_STATUS_SUCCESS;
+	tp_wma_handle wma = (tp_wma_handle) handle;
+
+	if (!wma || !wma->wmi_handle) {
+		WMA_LOGE(FL("WMA is closed, can not issue write APF mem"));
+		return QDF_STATUS_E_INVAL;
+	}
+
+	if (!WMI_SERVICE_IS_ENABLED(wma->wmi_service_bitmap,
+		WMI_SERVICE_BPF_OFFLOAD)) {
+		WMA_LOGE(FL("APF cababilities feature bit not enabled"));
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	if (wmi_unified_send_apf_write_work_memory_cmd(wma->wmi_handle,
+						       write_params)) {
+		WMA_LOGE(FL("Failed to send APF write mem command"));
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	WMA_LOGD("Sent APF wite mem on vdevid: %d", write_params->vdev_id);
+	return status;
+}
+
+int wma_apf_read_work_memory_event_handler(void *handle, uint8_t *evt_buf,
+					   uint32_t len)
+{
+	tp_wma_handle wma_handle;
+	wmi_unified_t wmi_handle;
+	struct wmi_apf_read_memory_resp_event_params evt_params = {0};
+	QDF_STATUS status;
+	tpAniSirGlobal pmac = cds_get_context(QDF_MODULE_ID_PE);
+
+	WMA_LOGI(FL("handle:%pK event:%pK len:%u"), handle, evt_buf, len);
+
+	wma_handle = handle;
+	if (!wma_handle) {
+		WMA_LOGE(FL("NULL wma_handle"));
+		return -EINVAL;
+	}
+
+	wmi_handle = wma_handle->wmi_handle;
+	if (!wmi_handle) {
+		WMA_LOGE(FL("NULL wmi_handle"));
+		return -EINVAL;
+	}
+
+	if (!pmac) {
+		WMA_LOGE(FL("Invalid pmac"));
+		return -EINVAL;
+	}
+
+	if (!pmac->sme.apf_read_mem_cb) {
+		WMA_LOGE(FL("Callback not registered"));
+		return -EINVAL;
+	}
+
+	status = wmi_extract_apf_read_memory_resp_event(wmi_handle,
+						evt_buf, &evt_params);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		WMA_LOGE(FL("Event extract failure: %d"), status);
+		return -EINVAL;
+	}
+
+	pmac->sme.apf_read_mem_cb(pmac->hHdd, &evt_params);
+
+	return 0;
+}
+
+QDF_STATUS wma_send_apf_read_work_memory_cmd(WMA_HANDLE handle,
+					     struct wmi_apf_read_memory_params
+								  *read_params)
+{
+	QDF_STATUS status = QDF_STATUS_SUCCESS;
+	tp_wma_handle wma = (tp_wma_handle) handle;
+
+	if (!wma || !wma->wmi_handle) {
+		WMA_LOGE(FL("WMA is closed, can not issue read APF memory"));
+		return QDF_STATUS_E_INVAL;
+	}
+
+	if (!WMI_SERVICE_IS_ENABLED(wma->wmi_service_bitmap,
+		WMI_SERVICE_BPF_OFFLOAD)) {
+		WMA_LOGE(FL("APF cababilities feature bit not enabled"));
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	if (wmi_unified_send_apf_read_work_memory_cmd(wma->wmi_handle,
+						      read_params)) {
+		WMA_LOGE(FL("Failed to send APF read memory command"));
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	WMA_LOGD("Sent APF read memory on vdevid: %d", read_params->vdev_id);
+	return status;
+}
+#endif /* FEATURE_WLAN_APF */
+
 /**
  * wma_set_tx_rx_aggregation_size() - sets tx rx aggregation sizes
  * @tx_rx_aggregation_size: aggregation size parameters

+ 25 - 4
core/wma/src/wma_main.c

@@ -3033,6 +3033,30 @@ static void wma_register_stats_events(wmi_unified_t wmi_handle)
 }
 #endif
 
+#ifdef FEATURE_WLAN_APF
+static void wma_register_apf_events(tp_wma_handle wma_handle)
+{
+	if (!wma_handle) {
+		QDF_TRACE(QDF_MODULE_ID_WMI, QDF_TRACE_LEVEL_INFO,
+			  "wma_handle is NULL\n");
+		return;
+	}
+
+	wmi_unified_register_event_handler(wma_handle->wmi_handle,
+					   wmi_apf_capability_info_event_id,
+					   wma_get_apf_caps_event_handler,
+					   WMA_RX_SERIALIZER_CTX);
+	wmi_unified_register_event_handler(wma_handle->wmi_handle,
+				wmi_apf_get_vdev_work_memory_resp_event_id,
+				wma_apf_read_work_memory_event_handler,
+				WMA_RX_SERIALIZER_CTX);
+}
+#else /* FEATURE_WLAN_APF */
+static void wma_register_apf_events(tp_wma_handle wma_handle)
+{
+}
+#endif /* FEATURE_WLAN_APF */
+
 /**
  * wma_open() - Allocate wma context and initialize it.
  * @cds_context:  cds context
@@ -3492,10 +3516,6 @@ QDF_STATUS wma_open(struct wlan_objmgr_psoc *psoc,
 					   wmi_peer_delete_response_event_id,
 					   wma_peer_delete_handler,
 					   WMA_RX_SERIALIZER_CTX);
-	wmi_unified_register_event_handler(wma_handle->wmi_handle,
-					   wmi_apf_capability_info_event_id,
-					   wma_get_apf_caps_event_handler,
-					   WMA_RX_SERIALIZER_CTX);
 	wmi_unified_register_event_handler(wma_handle->wmi_handle,
 					   wmi_chan_info_event_id,
 					   wma_chan_info_event_handler,
@@ -3571,6 +3591,7 @@ QDF_STATUS wma_open(struct wlan_objmgr_psoc *psoc,
 					   WMA_RX_SERIALIZER_CTX);
 #endif
 
+	wma_register_apf_events(wma_handle);
 
 	return QDF_STATUS_SUCCESS;