1  /*
2   * Copyright (c) 2012-2021 The Linux Foundation. All rights reserved.
3   * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
4   *
5   * Permission to use, copy, modify, and/or distribute this software for
6   * any purpose with or without fee is hereby granted, provided that the
7   * above copyright notice and this permission notice appear in all
8   * copies.
9   *
10   * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
11   * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
12   * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
13   * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
14   * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
15   * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
16   * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17   * PERFORMANCE OF THIS SOFTWARE.
18   */
19  
20  /**
21   * DOC: wlan_hdd_apf.c
22   *
23   * Android Packet Filter support and implementation
24   */
25  
26  #include "wlan_hdd_apf.h"
27  #include "osif_sync.h"
28  #include "qca_vendor.h"
29  #include "wlan_osif_request_manager.h"
30  
31  /*
32   * define short names for the global vendor params
33   * used by __wlan_hdd_cfg80211_apf_offload()
34   */
35  #define APF_INVALID \
36  	QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_INVALID
37  #define APF_SUBCMD \
38  	QCA_WLAN_VENDOR_ATTR_SET_RESET_PACKET_FILTER
39  #define APF_VERSION \
40  	QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_VERSION
41  #define APF_FILTER_ID \
42  	QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_ID
43  #define APF_PACKET_SIZE \
44  	QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_SIZE
45  #define APF_CURRENT_OFFSET \
46  	QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_CURRENT_OFFSET
47  #define APF_PROGRAM \
48  	QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_PROGRAM
49  #define APF_PROG_LEN \
50  	QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_PROG_LENGTH
51  #define APF_MAX \
52  	QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_MAX
53  
54  const struct nla_policy wlan_hdd_apf_offload_policy[APF_MAX + 1] = {
55  	[APF_SUBCMD] = {.type = NLA_U32},
56  	[APF_VERSION] = {.type = NLA_U32},
57  	[APF_FILTER_ID] = {.type = NLA_U32},
58  	[APF_PACKET_SIZE] = {.type = NLA_U32},
59  	[APF_CURRENT_OFFSET] = {.type = NLA_U32},
60  	[APF_PROGRAM] = {.type = NLA_BINARY,
61  			 .len = MAX_APF_MEMORY_LEN},
62  	[APF_PROG_LEN] = {.type = NLA_U32},
63  };
64  
hdd_apf_context_init(struct hdd_adapter * adapter)65  void hdd_apf_context_init(struct hdd_adapter *adapter)
66  {
67  	qdf_event_create(&adapter->apf_context.qdf_apf_event);
68  	qdf_spinlock_create(&adapter->apf_context.lock);
69  	adapter->apf_context.apf_enabled = true;
70  }
71  
hdd_apf_context_destroy(struct hdd_adapter * adapter)72  void hdd_apf_context_destroy(struct hdd_adapter *adapter)
73  {
74  	qdf_event_destroy(&adapter->apf_context.qdf_apf_event);
75  	qdf_spinlock_destroy(&adapter->apf_context.lock);
76  	qdf_mem_zero(&adapter->apf_context,
77  		     sizeof(struct hdd_apf_context));
78  }
79  
80  struct apf_offload_priv {
81  	struct sir_apf_get_offload apf_get_offload;
82  };
83  
hdd_get_apf_capabilities_cb(void * context,struct sir_apf_get_offload * data)84  void hdd_get_apf_capabilities_cb(void *context,
85  				 struct sir_apf_get_offload *data)
86  {
87  	struct osif_request *request;
88  	struct apf_offload_priv *priv;
89  
90  	hdd_enter();
91  
92  	request = osif_request_get(context);
93  	if (!request) {
94  		hdd_err("Obsolete request");
95  		return;
96  	}
97  
98  	priv = osif_request_priv(request);
99  	priv->apf_get_offload = *data;
100  	osif_request_complete(request);
101  	osif_request_put(request);
102  
103  	hdd_exit();
104  }
105  
106  /**
107   * hdd_post_get_apf_capabilities_rsp() - Callback function to APF Offload
108   * @hdd_ctx: hdd_context
109   * @apf_get_offload: struct for get offload
110   *
111   * Return: 0 on success, error number otherwise.
112   */
113  static int
hdd_post_get_apf_capabilities_rsp(struct hdd_context * hdd_ctx,struct sir_apf_get_offload * apf_get_offload)114  hdd_post_get_apf_capabilities_rsp(struct hdd_context *hdd_ctx,
115  				  struct sir_apf_get_offload *apf_get_offload)
116  {
117  	struct sk_buff *skb;
118  	uint32_t nl_buf_len;
119  
120  	hdd_enter();
121  
122  	nl_buf_len = NLMSG_HDRLEN;
123  	nl_buf_len +=
124  		(sizeof(apf_get_offload->max_bytes_for_apf_inst) + NLA_HDRLEN) +
125  		(sizeof(apf_get_offload->apf_version) + NLA_HDRLEN);
126  	skb = wlan_cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy,
127  						       nl_buf_len);
128  	if (!skb) {
129  		hdd_err("wlan_cfg80211_vendor_cmd_alloc_reply_skb failed");
130  		return -ENOMEM;
131  	}
132  
133  	hdd_ctx->apf_version = apf_get_offload->apf_version;
134  	hdd_debug("APF Version: %u APF max bytes: %u",
135  		  apf_get_offload->apf_version,
136  		  apf_get_offload->max_bytes_for_apf_inst);
137  
138  	if (nla_put_u32(skb, APF_PACKET_SIZE,
139  			apf_get_offload->max_bytes_for_apf_inst) ||
140  	    nla_put_u32(skb, APF_VERSION, apf_get_offload->apf_version)) {
141  		hdd_err("nla put failure");
142  		goto nla_put_failure;
143  	}
144  
145  	wlan_cfg80211_vendor_cmd_reply(skb);
146  	hdd_exit();
147  	return 0;
148  
149  nla_put_failure:
150  	wlan_cfg80211_vendor_free_skb(skb);
151  	return -EINVAL;
152  }
153  
154  /**
155   * hdd_get_apf_capabilities - Get APF offload Capabilities
156   * @hdd_ctx: Hdd context
157   *
158   * Return: 0 on success, errno on failure
159   */
hdd_get_apf_capabilities(struct hdd_context * hdd_ctx)160  static int hdd_get_apf_capabilities(struct hdd_context *hdd_ctx)
161  {
162  	QDF_STATUS status;
163  	int ret;
164  	void *cookie;
165  	struct osif_request *request;
166  	struct apf_offload_priv *priv;
167  	static const struct osif_request_params params = {
168  		.priv_size = sizeof(*priv),
169  		.timeout_ms = WLAN_WAIT_TIME_APF,
170  	};
171  
172  	hdd_enter();
173  
174  	request = osif_request_alloc(&params);
175  	if (!request) {
176  		hdd_err("Unable to allocate request");
177  		return -EINVAL;
178  	}
179  	cookie = osif_request_cookie(request);
180  
181  	status = sme_get_apf_capabilities(hdd_ctx->mac_handle,
182  					  hdd_get_apf_capabilities_cb,
183  					  cookie);
184  	if (!QDF_IS_STATUS_SUCCESS(status)) {
185  		hdd_err("Unable to retrieve APF caps");
186  		ret = qdf_status_to_os_return(status);
187  		goto cleanup;
188  	}
189  	ret = osif_request_wait_for_response(request);
190  	if (ret) {
191  		hdd_err("Target response timed out");
192  		goto cleanup;
193  	}
194  	priv = osif_request_priv(request);
195  	ret = hdd_post_get_apf_capabilities_rsp(hdd_ctx,
196  						&priv->apf_get_offload);
197  	if (ret)
198  		hdd_err("Failed to post get apf capabilities");
199  
200  cleanup:
201  	/*
202  	 * either we never sent a request to SME, we sent a request to
203  	 * SME and timed out, or we sent a request to SME, received a
204  	 * response from SME, and posted the response to userspace.
205  	 * regardless we are done with the request.
206  	 */
207  	osif_request_put(request);
208  	hdd_exit();
209  
210  	return ret;
211  }
212  
213  /**
214   * hdd_set_reset_apf_offload - Post set/reset apf to SME
215   * @hdd_ctx: Hdd context
216   * @tb: Length of @data
217   * @adapter: pointer to adapter struct
218   *
219   * Return: 0 on success; errno on failure
220   */
hdd_set_reset_apf_offload(struct hdd_context * hdd_ctx,struct nlattr ** tb,struct hdd_adapter * adapter)221  static int hdd_set_reset_apf_offload(struct hdd_context *hdd_ctx,
222  				     struct nlattr **tb,
223  				     struct hdd_adapter *adapter)
224  {
225  	struct sir_apf_set_offload apf_set_offload = {0};
226  	QDF_STATUS status;
227  	int prog_len;
228  	int ret = 0;
229  
230  	if (!hdd_cm_is_vdev_associated(adapter->deflink)) {
231  		hdd_err("Not in Connected state!");
232  		return -ENOTSUPP;
233  	}
234  
235  	/* Parse and fetch apf packet size */
236  	if (!tb[APF_PACKET_SIZE]) {
237  		hdd_err("attr apf packet size failed");
238  		ret = -EINVAL;
239  		goto fail;
240  	}
241  
242  	apf_set_offload.session_id = adapter->deflink->vdev_id;
243  	apf_set_offload.total_length = nla_get_u32(tb[APF_PACKET_SIZE]);
244  
245  	if (!apf_set_offload.total_length) {
246  		hdd_debug("APF reset packet");
247  		goto post_sme;
248  	}
249  
250  	/* Parse and fetch apf program */
251  	if (!tb[APF_PROGRAM]) {
252  		hdd_err("attr apf program failed");
253  		ret = -EINVAL;
254  		goto fail;
255  	}
256  
257  	prog_len = nla_len(tb[APF_PROGRAM]);
258  	apf_set_offload.program = qdf_mem_malloc(sizeof(uint8_t) * prog_len);
259  
260  	if (!apf_set_offload.program) {
261  		ret = -ENOMEM;
262  		goto fail;
263  	}
264  
265  	apf_set_offload.current_length = prog_len;
266  	nla_memcpy(apf_set_offload.program, tb[APF_PROGRAM], prog_len);
267  
268  	/* Parse and fetch filter Id */
269  	if (!tb[APF_FILTER_ID]) {
270  		hdd_err("attr filter id failed");
271  		ret = -EINVAL;
272  		goto fail;
273  	}
274  	apf_set_offload.filter_id = nla_get_u32(tb[APF_FILTER_ID]);
275  
276  	/* Parse and fetch current offset */
277  	if (!tb[APF_CURRENT_OFFSET]) {
278  		hdd_err("attr current offset failed");
279  		ret = -EINVAL;
280  		goto fail;
281  	}
282  	apf_set_offload.current_offset = nla_get_u32(tb[APF_CURRENT_OFFSET]);
283  
284  post_sme:
285  	hdd_debug("Posting, session_id: %d APF Version: %d filter ID: %d total_len: %d current_len: %d offset: %d",
286  		  apf_set_offload.session_id, apf_set_offload.version,
287  		  apf_set_offload.filter_id, apf_set_offload.total_length,
288  		  apf_set_offload.current_length,
289  		  apf_set_offload.current_offset);
290  
291  	status = sme_set_apf_instructions(hdd_ctx->mac_handle,
292  					  &apf_set_offload);
293  	if (!QDF_IS_STATUS_SUCCESS(status)) {
294  		hdd_err("sme_set_apf_instructions failed(err=%d)", status);
295  		ret = -EINVAL;
296  		goto fail;
297  	}
298  	hdd_exit();
299  
300  fail:
301  	if (apf_set_offload.current_length)
302  		qdf_mem_free(apf_set_offload.program);
303  
304  	if (!ret)
305  		hdd_ctx->apf_enabled_v2 = true;
306  
307  	return ret;
308  }
309  
310  /**
311   * hdd_enable_disable_apf - Enable or Disable the APF interpreter
312   * @adapter: HDD Adapter
313   * @apf_enable: true: Enable APF Int., false: disable APF Int.
314   *
315   * Return: 0 on success, errno on failure
316   */
317  static int
hdd_enable_disable_apf(struct hdd_adapter * adapter,bool apf_enable)318  hdd_enable_disable_apf(struct hdd_adapter *adapter, bool apf_enable)
319  {
320  	QDF_STATUS status;
321  
322  	status = sme_set_apf_enable_disable(hdd_adapter_get_mac_handle(adapter),
323  					    adapter->deflink->vdev_id,
324  					    apf_enable);
325  	if (!QDF_IS_STATUS_SUCCESS(status)) {
326  		hdd_err("Unable to post sme apf enable/disable message (status-%d)",
327  				status);
328  		return -EINVAL;
329  	}
330  
331  	adapter->apf_context.apf_enabled = apf_enable;
332  
333  	return 0;
334  }
335  
336  /**
337   * hdd_apf_write_memory - Write into the apf work memory
338   * @adapter: HDD Adapter
339   * @tb: list of attributes
340   *
341   * This function writes code/data into the APF work memory and
342   * provides program length that is passed on to the interpreter.
343   *
344   * Return: 0 on success, errno on failure
345   */
346  static int
hdd_apf_write_memory(struct hdd_adapter * adapter,struct nlattr ** tb)347  hdd_apf_write_memory(struct hdd_adapter *adapter, struct nlattr **tb)
348  {
349  	struct wmi_apf_write_memory_params write_mem_params = {0};
350  	struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
351  	QDF_STATUS status;
352  	int ret = 0;
353  
354  	write_mem_params.vdev_id = adapter->deflink->vdev_id;
355  	if (adapter->apf_context.apf_enabled) {
356  		hdd_err("Cannot get/set when APF interpreter is enabled");
357  		return -EINVAL;
358  	}
359  
360  	/* Read program length */
361  	if (!tb[APF_PROG_LEN]) {
362  		hdd_err("attr program length failed");
363  		return -EINVAL;
364  	}
365  	write_mem_params.program_len = nla_get_u32(tb[APF_PROG_LEN]);
366  
367  	/* Read APF work memory offset */
368  	if (!tb[APF_CURRENT_OFFSET]) {
369  		hdd_err("attr apf packet size failed");
370  		return -EINVAL;
371  	}
372  	write_mem_params.addr_offset = nla_get_u32(tb[APF_CURRENT_OFFSET]);
373  
374  	/* Parse and fetch apf program */
375  	if (!tb[APF_PROGRAM]) {
376  		hdd_err("attr apf program failed");
377  		return -EINVAL;
378  	}
379  
380  	write_mem_params.length = nla_len(tb[APF_PROGRAM]);
381  	if (!write_mem_params.length) {
382  		hdd_err("Program attr with empty data");
383  		return -EINVAL;
384  	}
385  
386  	write_mem_params.buf = qdf_mem_malloc(sizeof(uint8_t)
387  						* write_mem_params.length);
388  	if (!write_mem_params.buf)
389  		return -EINVAL;
390  	nla_memcpy(write_mem_params.buf, tb[APF_PROGRAM],
391  		   write_mem_params.length);
392  
393  	write_mem_params.apf_version = hdd_ctx->apf_version;
394  
395  	status = sme_apf_write_work_memory(hdd_adapter_get_mac_handle(adapter),
396  					   &write_mem_params);
397  	if (!QDF_IS_STATUS_SUCCESS(status)) {
398  		hdd_err("Unable to retrieve APF caps");
399  		ret = -EINVAL;
400  	}
401  
402  	hdd_debug("Writing successful into APF work memory from offset 0x%X:",
403  		  write_mem_params.addr_offset);
404  	QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_DEBUG,
405  			   write_mem_params.buf, write_mem_params.length);
406  
407  	if (write_mem_params.buf)
408  		qdf_mem_free(write_mem_params.buf);
409  
410  	return ret;
411  }
412  
413  /**
414   * hdd_apf_read_memory_callback - HDD Callback for the APF read memory
415   *	operation
416   * @hdd_context: Hdd context
417   * @evt: APF read memory event response parameters
418   *
419   * Return: 0 on success, errno on failure
420   */
421  static void
hdd_apf_read_memory_callback(void * hdd_context,struct wmi_apf_read_memory_resp_event_params * evt)422  hdd_apf_read_memory_callback(void *hdd_context,
423  			     struct wmi_apf_read_memory_resp_event_params *evt)
424  {
425  	struct hdd_context *hdd_ctx = hdd_context;
426  	struct hdd_apf_context *context;
427  	uint8_t *buf_ptr;
428  	uint32_t pkt_offset;
429  	struct wlan_hdd_link_info *link_info;
430  
431  	hdd_enter();
432  
433  	if (wlan_hdd_validate_context(hdd_ctx) || !evt) {
434  		hdd_err("HDD context is invalid or event buf(%pK) is null",
435  			evt);
436  		return;
437  	}
438  
439  	link_info = hdd_get_link_info_by_vdev(hdd_ctx, evt->vdev_id);
440  	if (!link_info || hdd_validate_adapter(link_info->adapter))
441  		return;
442  
443  	context = &link_info->adapter->apf_context;
444  
445  	if (context->magic != APF_CONTEXT_MAGIC) {
446  		/* The caller presumably timed out, nothing to do */
447  		hdd_err("Caller timed out or corrupt magic, simply return");
448  		return;
449  	}
450  
451  	if (evt->offset <  context->offset) {
452  		hdd_err("Offset in read event(%d) smaller than offset in request(%d)!",
453  					evt->offset, context->offset);
454  		return;
455  	}
456  
457  	/*
458  	 * offset in the event is relative to the APF work memory.
459  	 * Calculate the packet offset, which gives us the relative
460  	 * location in the buffer to start copy into.
461  	 */
462  	pkt_offset = evt->offset - context->offset;
463  
464  	if ((pkt_offset > context->buf_len) ||
465  	    (context->buf_len - pkt_offset < evt->length)) {
466  		hdd_err("Read chunk exceeding allocated space");
467  		return;
468  	}
469  	buf_ptr = context->buf + pkt_offset;
470  
471  	qdf_mem_copy(buf_ptr, evt->data, evt->length);
472  
473  	if (!evt->more_data) {
474  		/* Release the caller after last event, clear magic */
475  		context->magic = 0;
476  		qdf_event_set(&context->qdf_apf_event);
477  	}
478  
479  	hdd_exit();
480  }
481  
482  /**
483   * hdd_apf_read_memory - Read part of the apf work memory
484   * @adapter: HDD Adapter
485   * @tb: list of attributes
486   *
487   * Return: 0 on success, errno on failure
488   */
hdd_apf_read_memory(struct hdd_adapter * adapter,struct nlattr ** tb)489  static int hdd_apf_read_memory(struct hdd_adapter *adapter, struct nlattr **tb)
490  {
491  	struct wmi_apf_read_memory_params read_mem_params = {0};
492  	struct hdd_apf_context *context = &adapter->apf_context;
493  	struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
494  	QDF_STATUS status;
495  	unsigned long nl_buf_len = NLMSG_HDRLEN;
496  	int ret = 0;
497  	struct sk_buff *skb = NULL;
498  	uint8_t *bufptr;
499  	mac_handle_t mac_handle;
500  
501  	mac_handle = hdd_adapter_get_mac_handle(adapter);
502  	if (!mac_handle) {
503  		hdd_debug("mac ctx NULL");
504  		return -EINVAL;
505  	}
506  
507  	if (context->apf_enabled) {
508  		hdd_err("Cannot get/set while interpreter is enabled");
509  		return -EINVAL;
510  	}
511  
512  	read_mem_params.vdev_id = adapter->deflink->vdev_id;
513  
514  	/* Read APF work memory offset */
515  	if (!tb[APF_CURRENT_OFFSET]) {
516  		hdd_err("attr apf memory offset failed");
517  		return -EINVAL;
518  	}
519  	read_mem_params.addr_offset = nla_get_u32(tb[APF_CURRENT_OFFSET]);
520  	if (read_mem_params.addr_offset > MAX_APF_MEMORY_LEN) {
521  		hdd_err("attr apf memory offset should be less than %d",
522  			MAX_APF_MEMORY_LEN);
523  		return -EINVAL;
524  	}
525  
526  	/* Read length */
527  	if (!tb[APF_PACKET_SIZE]) {
528  		hdd_err("attr apf packet size failed");
529  		return -EINVAL;
530  	}
531  	read_mem_params.length = nla_get_u32(tb[APF_PACKET_SIZE]);
532  	if (!read_mem_params.length) {
533  		hdd_err("apf read length cannot be zero!");
534  		return -EINVAL;
535  	}
536  	bufptr = qdf_mem_malloc(read_mem_params.length);
537  	if (!bufptr)
538  		return -ENOMEM;
539  
540  	qdf_event_reset(&context->qdf_apf_event);
541  	context->offset = read_mem_params.addr_offset;
542  
543  	context->buf = bufptr;
544  	context->buf_len = read_mem_params.length;
545  	context->magic = APF_CONTEXT_MAGIC;
546  
547  	status = sme_apf_read_work_memory(mac_handle, &read_mem_params,
548  					  hdd_apf_read_memory_callback);
549  	if (QDF_IS_STATUS_ERROR(status)) {
550  		hdd_err("Unable to post sme APF read memory message (status-%d)",
551  				status);
552  		ret = -EINVAL;
553  		goto fail;
554  	}
555  
556  	/* request was sent -- wait for the response */
557  	status = qdf_wait_for_event_completion(&context->qdf_apf_event,
558  					       WLAN_WAIT_TIME_APF_READ_MEM);
559  	if (QDF_IS_STATUS_ERROR(status)) {
560  		hdd_err("Target response timed out");
561  		context->magic = 0;
562  		ret = -ETIMEDOUT;
563  		goto fail;
564  	}
565  
566  	nl_buf_len += sizeof(uint32_t) + NLA_HDRLEN;
567  	nl_buf_len += context->buf_len + NLA_HDRLEN;
568  	skb = wlan_cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy,
569  						       nl_buf_len);
570  	if (!skb) {
571  		hdd_err("wlan_cfg80211_vendor_cmd_alloc_reply_skb failed");
572  		ret = -ENOMEM;
573  		goto fail;
574  	}
575  
576  	if (nla_put_u32(skb, APF_SUBCMD, QCA_WLAN_READ_PACKET_FILTER) ||
577  	    nla_put(skb, APF_PROGRAM, read_mem_params.length, context->buf)) {
578  		hdd_err("put fail");
579  		wlan_cfg80211_vendor_free_skb(skb);
580  		ret = -EINVAL;
581  		goto fail;
582  	}
583  
584  	wlan_cfg80211_vendor_cmd_reply(skb);
585  
586  	hdd_debug("Reading APF work memory from offset 0x%X:",
587  		  read_mem_params.addr_offset);
588  	QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_DEBUG,
589  			   context->buf, read_mem_params.length);
590  fail:
591  	if (context->buf) {
592  		qdf_mem_free(context->buf);
593  		context->buf = NULL;
594  	}
595  
596  	return ret;
597  }
598  
599  /**
600   * __wlan_hdd_cfg80211_apf_offload() - Set/Reset to APF Offload
601   * @wiphy:    wiphy structure pointer
602   * @wdev:     Wireless device structure pointer
603   * @data:     Pointer to the data received
604   * @data_len: Length of @data
605   *
606   * Return: 0 on success; errno on failure
607   */
608  static int
__wlan_hdd_cfg80211_apf_offload(struct wiphy * wiphy,struct wireless_dev * wdev,const void * data,int data_len)609  __wlan_hdd_cfg80211_apf_offload(struct wiphy *wiphy,
610  				struct wireless_dev *wdev,
611  				const void *data, int data_len)
612  {
613  	struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
614  	struct net_device *dev = wdev->netdev;
615  	struct hdd_adapter *adapter =  WLAN_HDD_GET_PRIV_PTR(dev);
616  	struct nlattr *tb[APF_MAX + 1];
617  	int ret_val = 0, apf_subcmd;
618  	struct hdd_apf_context *context;
619  
620  	hdd_enter_dev(dev);
621  
622  	if (!adapter) {
623  		hdd_err("Adapter is null");
624  		return -EINVAL;
625  	}
626  
627  	context = &adapter->apf_context;
628  
629  	ret_val = wlan_hdd_validate_context(hdd_ctx);
630  	if (ret_val)
631  		return ret_val;
632  
633  	if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
634  		hdd_err("Command not allowed in FTM mode");
635  		return -EINVAL;
636  	}
637  
638  	if (!ucfg_pmo_is_apf_enabled(hdd_ctx->psoc)) {
639  		hdd_err("APF is not supported or disabled through INI");
640  		return -ENOTSUPP;
641  	}
642  
643  	if (!(adapter->device_mode == QDF_STA_MODE ||
644  	      adapter->device_mode == QDF_P2P_CLIENT_MODE)) {
645  		hdd_err("APF only supported in STA or P2P CLI modes!");
646  		return -ENOTSUPP;
647  	}
648  
649  	if (wlan_cfg80211_nla_parse(tb, APF_MAX, data, data_len,
650  				    wlan_hdd_apf_offload_policy)) {
651  		hdd_err("Invalid ATTR");
652  		return -EINVAL;
653  	}
654  
655  	if (!tb[APF_SUBCMD]) {
656  		hdd_err("attr apf sub-command failed");
657  		return -EINVAL;
658  	}
659  	apf_subcmd = nla_get_u32(tb[APF_SUBCMD]);
660  
661  	/* Do not allow simultaneous new APF commands on the same adapter */
662  	qdf_spin_lock(&context->lock);
663  	if (context->cmd_in_progress) {
664  		qdf_spin_unlock(&context->lock);
665  		hdd_err("Another cmd in progress for same session!");
666  		return -EAGAIN;
667  	}
668  	context->cmd_in_progress = true;
669  	qdf_spin_unlock(&context->lock);
670  
671  	switch (apf_subcmd) {
672  	/* Legacy APF sub-commands */
673  	case QCA_WLAN_SET_PACKET_FILTER:
674  		ret_val = hdd_set_reset_apf_offload(hdd_ctx, tb,
675  						    adapter);
676  		break;
677  	case QCA_WLAN_GET_PACKET_FILTER:
678  		ret_val = hdd_get_apf_capabilities(hdd_ctx);
679  		break;
680  
681  	/* APF 3.0 sub-commands */
682  	case QCA_WLAN_WRITE_PACKET_FILTER:
683  		ret_val = hdd_apf_write_memory(adapter, tb);
684  		break;
685  	case QCA_WLAN_READ_PACKET_FILTER:
686  		ret_val = hdd_apf_read_memory(adapter, tb);
687  		break;
688  	case QCA_WLAN_ENABLE_PACKET_FILTER:
689  		ret_val = hdd_enable_disable_apf(adapter, true);
690  		break;
691  	case QCA_WLAN_DISABLE_PACKET_FILTER:
692  		ret_val = hdd_enable_disable_apf(adapter, false);
693  		break;
694  	default:
695  		hdd_err("Unknown APF Sub-command: %d", apf_subcmd);
696  		ret_val = -ENOTSUPP;
697  	}
698  
699  	qdf_spin_lock(&context->lock);
700  	context->cmd_in_progress = false;
701  	qdf_spin_unlock(&context->lock);
702  
703  	return ret_val;
704  }
705  
706  int
wlan_hdd_cfg80211_apf_offload(struct wiphy * wiphy,struct wireless_dev * wdev,const void * data,int data_len)707  wlan_hdd_cfg80211_apf_offload(struct wiphy *wiphy, struct wireless_dev *wdev,
708  			      const void *data, int data_len)
709  {
710  	int errno;
711  	struct osif_vdev_sync *vdev_sync;
712  
713  	errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync);
714  	if (errno)
715  		return errno;
716  
717  	errno = __wlan_hdd_cfg80211_apf_offload(wiphy, wdev, data, data_len);
718  
719  	osif_vdev_sync_op_stop(vdev_sync);
720  
721  	return errno;
722  }
723  
724