xref: /wlan-dirver/qcacld-3.0/core/hdd/src/wlan_hdd_apf.c (revision a24510696edc222f9e9b89d388912a346710831d)
1 /*
2  * Copyright (c) 2012-2021 The Linux Foundation. All rights reserved.
3  * Copyright (c) 2022 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 
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 
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 
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_context: hdd_context
109  * @apf_get_offload: struct for get offload
110  *
111  * Return: 0 on success, error number otherwise.
112  */
113 static int
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 
127 	skb = cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy, nl_buf_len);
128 	if (!skb) {
129 		hdd_err("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 	cfg80211_vendor_cmd_reply(skb);
146 	hdd_exit();
147 	return 0;
148 
149 nla_put_failure:
150 	kfree_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  */
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  */
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)) {
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->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
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->vdev_id, apf_enable);
324 	if (!QDF_IS_STATUS_SUCCESS(status)) {
325 		hdd_err("Unable to post sme apf enable/disable message (status-%d)",
326 				status);
327 		return -EINVAL;
328 	}
329 
330 	adapter->apf_context.apf_enabled = apf_enable;
331 
332 	return 0;
333 }
334 
335 /**
336  * hdd_apf_write_memory - Write into the apf work memory
337  * @adapter: HDD Adapter
338  * @tb: list of attributes
339  *
340  * This function writes code/data into the APF work memory and
341  * provides program length that is passed on to the interpreter.
342  *
343  * Return: 0 on success, errno on failure
344  */
345 static int
346 hdd_apf_write_memory(struct hdd_adapter *adapter, struct nlattr **tb)
347 {
348 	struct wmi_apf_write_memory_params write_mem_params = {0};
349 	struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
350 	QDF_STATUS status;
351 	int ret = 0;
352 
353 	write_mem_params.vdev_id = adapter->vdev_id;
354 	if (adapter->apf_context.apf_enabled) {
355 		hdd_err("Cannot get/set when APF interpreter is enabled");
356 		return -EINVAL;
357 	}
358 
359 	/* Read program length */
360 	if (!tb[APF_PROG_LEN]) {
361 		hdd_err("attr program length failed");
362 		return -EINVAL;
363 	}
364 	write_mem_params.program_len = nla_get_u32(tb[APF_PROG_LEN]);
365 
366 	/* Read APF work memory offset */
367 	if (!tb[APF_CURRENT_OFFSET]) {
368 		hdd_err("attr apf packet size failed");
369 		return -EINVAL;
370 	}
371 	write_mem_params.addr_offset = nla_get_u32(tb[APF_CURRENT_OFFSET]);
372 
373 	/* Parse and fetch apf program */
374 	if (!tb[APF_PROGRAM]) {
375 		hdd_err("attr apf program failed");
376 		return -EINVAL;
377 	}
378 
379 	write_mem_params.length = nla_len(tb[APF_PROGRAM]);
380 	if (!write_mem_params.length) {
381 		hdd_err("Program attr with empty data");
382 		return -EINVAL;
383 	}
384 
385 	write_mem_params.buf = qdf_mem_malloc(sizeof(uint8_t)
386 						* write_mem_params.length);
387 	if (!write_mem_params.buf)
388 		return -EINVAL;
389 	nla_memcpy(write_mem_params.buf, tb[APF_PROGRAM],
390 		   write_mem_params.length);
391 
392 	write_mem_params.apf_version = hdd_ctx->apf_version;
393 
394 	status = sme_apf_write_work_memory(hdd_adapter_get_mac_handle(adapter),
395 					   &write_mem_params);
396 	if (!QDF_IS_STATUS_SUCCESS(status)) {
397 		hdd_err("Unable to retrieve APF caps");
398 		ret = -EINVAL;
399 	}
400 
401 	hdd_debug("Writing successful into APF work memory from offset 0x%X:",
402 		  write_mem_params.addr_offset);
403 	QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_DEBUG,
404 			   write_mem_params.buf, write_mem_params.length);
405 
406 	if (write_mem_params.buf)
407 		qdf_mem_free(write_mem_params.buf);
408 
409 	return ret;
410 }
411 
412 /**
413  * hdd_apf_read_memory_callback - HDD Callback for the APF read memory
414  *	operation
415  * @context: Hdd context
416  * @evt: APF read memory event response parameters
417  *
418  * Return: 0 on success, errno on failure
419  */
420 static void
421 hdd_apf_read_memory_callback(void *hdd_context,
422 			     struct wmi_apf_read_memory_resp_event_params *evt)
423 {
424 	struct hdd_context *hdd_ctx = hdd_context;
425 	struct hdd_adapter *adapter;
426 	struct hdd_apf_context *context;
427 	uint8_t *buf_ptr;
428 	uint32_t pkt_offset;
429 
430 	hdd_enter();
431 
432 	if (wlan_hdd_validate_context(hdd_ctx) || !evt) {
433 		hdd_err("HDD context is invalid or event buf(%pK) is null",
434 			evt);
435 		return;
436 	}
437 
438 	adapter = hdd_get_adapter_by_vdev(hdd_ctx, evt->vdev_id);
439 	if (hdd_validate_adapter(adapter))
440 		return;
441 	context = &adapter->apf_context;
442 
443 	if (context->magic != APF_CONTEXT_MAGIC) {
444 		/* The caller presumably timed out, nothing to do */
445 		hdd_err("Caller timed out or corrupt magic, simply return");
446 		return;
447 	}
448 
449 	if (evt->offset <  context->offset) {
450 		hdd_err("Offset in read event(%d) smaller than offset in request(%d)!",
451 					evt->offset, context->offset);
452 		return;
453 	}
454 
455 	/*
456 	 * offset in the event is relative to the APF work memory.
457 	 * Calculate the packet offset, which gives us the relative
458 	 * location in the buffer to start copy into.
459 	 */
460 	pkt_offset = evt->offset - context->offset;
461 
462 	if ((pkt_offset > context->buf_len) ||
463 	    (context->buf_len - pkt_offset < evt->length)) {
464 		hdd_err("Read chunk exceeding allocated space");
465 		return;
466 	}
467 	buf_ptr = context->buf + pkt_offset;
468 
469 	qdf_mem_copy(buf_ptr, evt->data, evt->length);
470 
471 	if (!evt->more_data) {
472 		/* Release the caller after last event, clear magic */
473 		context->magic = 0;
474 		qdf_event_set(&context->qdf_apf_event);
475 	}
476 
477 	hdd_exit();
478 }
479 
480 /**
481  * hdd_apf_read_memory - Read part of the apf work memory
482  * @adapter: HDD Adapter
483  * @tb: list of attributes
484  *
485  * Return: 0 on success, errno on failure
486  */
487 static int hdd_apf_read_memory(struct hdd_adapter *adapter, struct nlattr **tb)
488 {
489 	struct wmi_apf_read_memory_params read_mem_params = {0};
490 	struct hdd_apf_context *context = &adapter->apf_context;
491 	struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
492 	QDF_STATUS status;
493 	unsigned long nl_buf_len = NLMSG_HDRLEN;
494 	int ret = 0;
495 	struct sk_buff *skb = NULL;
496 	uint8_t *bufptr;
497 
498 	if (context->apf_enabled) {
499 		hdd_err("Cannot get/set while interpreter is enabled");
500 		return -EINVAL;
501 	}
502 
503 	read_mem_params.vdev_id = adapter->vdev_id;
504 
505 	/* Read APF work memory offset */
506 	if (!tb[APF_CURRENT_OFFSET]) {
507 		hdd_err("attr apf memory offset failed");
508 		return -EINVAL;
509 	}
510 	read_mem_params.addr_offset = nla_get_u32(tb[APF_CURRENT_OFFSET]);
511 	if (read_mem_params.addr_offset > MAX_APF_MEMORY_LEN) {
512 		hdd_err("attr apf memory offset should be less than %d",
513 			MAX_APF_MEMORY_LEN);
514 		return -EINVAL;
515 	}
516 
517 	/* Read length */
518 	if (!tb[APF_PACKET_SIZE]) {
519 		hdd_err("attr apf packet size failed");
520 		return -EINVAL;
521 	}
522 	read_mem_params.length = nla_get_u32(tb[APF_PACKET_SIZE]);
523 	if (!read_mem_params.length) {
524 		hdd_err("apf read length cannot be zero!");
525 		return -EINVAL;
526 	}
527 	bufptr = qdf_mem_malloc(read_mem_params.length);
528 	if (!bufptr)
529 		return -ENOMEM;
530 
531 	qdf_event_reset(&context->qdf_apf_event);
532 	context->offset = read_mem_params.addr_offset;
533 
534 	context->buf = bufptr;
535 	context->buf_len = read_mem_params.length;
536 	context->magic = APF_CONTEXT_MAGIC;
537 
538 	status = sme_apf_read_work_memory(hdd_adapter_get_mac_handle(adapter),
539 					  &read_mem_params,
540 					  hdd_apf_read_memory_callback);
541 	if (QDF_IS_STATUS_ERROR(status)) {
542 		hdd_err("Unable to post sme APF read memory message (status-%d)",
543 				status);
544 		ret = -EINVAL;
545 		goto fail;
546 	}
547 
548 	/* request was sent -- wait for the response */
549 	status = qdf_wait_for_event_completion(&context->qdf_apf_event,
550 					       WLAN_WAIT_TIME_APF_READ_MEM);
551 	if (QDF_IS_STATUS_ERROR(status)) {
552 		hdd_err("Target response timed out");
553 		context->magic = 0;
554 		ret = -ETIMEDOUT;
555 		goto fail;
556 	}
557 
558 	nl_buf_len += sizeof(uint32_t) + NLA_HDRLEN;
559 	nl_buf_len += context->buf_len + NLA_HDRLEN;
560 
561 	skb = cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy, nl_buf_len);
562 	if (!skb) {
563 		hdd_err("cfg80211_vendor_cmd_alloc_reply_skb failed");
564 		ret = -ENOMEM;
565 		goto fail;
566 	}
567 
568 	if (nla_put_u32(skb, APF_SUBCMD, QCA_WLAN_READ_PACKET_FILTER) ||
569 	    nla_put(skb, APF_PROGRAM, read_mem_params.length, context->buf)) {
570 		hdd_err("put fail");
571 		kfree_skb(skb);
572 		ret = -EINVAL;
573 		goto fail;
574 	}
575 
576 	cfg80211_vendor_cmd_reply(skb);
577 
578 	hdd_debug("Reading APF work memory from offset 0x%X:",
579 		  read_mem_params.addr_offset);
580 	QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_DEBUG,
581 			   context->buf, read_mem_params.length);
582 fail:
583 	if (context->buf) {
584 		qdf_mem_free(context->buf);
585 		context->buf = NULL;
586 	}
587 
588 	return ret;
589 }
590 
591 /**
592  * wlan_hdd_cfg80211_apf_offload() - Set/Reset to APF Offload
593  * @wiphy:    wiphy structure pointer
594  * @wdev:     Wireless device structure pointer
595  * @data:     Pointer to the data received
596  * @data_len: Length of @data
597  *
598  * Return: 0 on success; errno on failure
599  */
600 static int
601 __wlan_hdd_cfg80211_apf_offload(struct wiphy *wiphy,
602 				struct wireless_dev *wdev,
603 				const void *data, int data_len)
604 {
605 	struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
606 	struct net_device *dev = wdev->netdev;
607 	struct hdd_adapter *adapter =  WLAN_HDD_GET_PRIV_PTR(dev);
608 	struct nlattr *tb[APF_MAX + 1];
609 	int ret_val = 0, apf_subcmd;
610 	struct hdd_apf_context *context;
611 
612 	hdd_enter_dev(dev);
613 
614 	if (!adapter) {
615 		hdd_err("Adapter is null");
616 		return -EINVAL;
617 	}
618 
619 	context = &adapter->apf_context;
620 
621 	ret_val = wlan_hdd_validate_context(hdd_ctx);
622 	if (ret_val)
623 		return ret_val;
624 
625 	if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
626 		hdd_err("Command not allowed in FTM mode");
627 		return -EINVAL;
628 	}
629 
630 	if (!ucfg_pmo_is_apf_enabled(hdd_ctx->psoc)) {
631 		hdd_err("APF is not supported or disabled through INI");
632 		return -ENOTSUPP;
633 	}
634 
635 	if (!(adapter->device_mode == QDF_STA_MODE ||
636 	      adapter->device_mode == QDF_P2P_CLIENT_MODE)) {
637 		hdd_err("APF only supported in STA or P2P CLI modes!");
638 		return -ENOTSUPP;
639 	}
640 
641 	if (wlan_cfg80211_nla_parse(tb, APF_MAX, data, data_len,
642 				    wlan_hdd_apf_offload_policy)) {
643 		hdd_err("Invalid ATTR");
644 		return -EINVAL;
645 	}
646 
647 	if (!tb[APF_SUBCMD]) {
648 		hdd_err("attr apf sub-command failed");
649 		return -EINVAL;
650 	}
651 	apf_subcmd = nla_get_u32(tb[APF_SUBCMD]);
652 
653 	/* Do not allow simultaneous new APF commands on the same adapter */
654 	qdf_spin_lock(&context->lock);
655 	if (context->cmd_in_progress) {
656 		qdf_spin_unlock(&context->lock);
657 		hdd_err("Another cmd in progress for same session!");
658 		return -EAGAIN;
659 	}
660 	context->cmd_in_progress = true;
661 	qdf_spin_unlock(&context->lock);
662 
663 	switch (apf_subcmd) {
664 	/* Legacy APF sub-commands */
665 	case QCA_WLAN_SET_PACKET_FILTER:
666 		ret_val = hdd_set_reset_apf_offload(hdd_ctx, tb,
667 						    adapter);
668 		break;
669 	case QCA_WLAN_GET_PACKET_FILTER:
670 		ret_val = hdd_get_apf_capabilities(hdd_ctx);
671 		break;
672 
673 	/* APF 3.0 sub-commands */
674 	case QCA_WLAN_WRITE_PACKET_FILTER:
675 		ret_val = hdd_apf_write_memory(adapter, tb);
676 		break;
677 	case QCA_WLAN_READ_PACKET_FILTER:
678 		ret_val = hdd_apf_read_memory(adapter, tb);
679 		break;
680 	case QCA_WLAN_ENABLE_PACKET_FILTER:
681 		ret_val = hdd_enable_disable_apf(adapter, true);
682 		break;
683 	case QCA_WLAN_DISABLE_PACKET_FILTER:
684 		ret_val = hdd_enable_disable_apf(adapter, false);
685 		break;
686 	default:
687 		hdd_err("Unknown APF Sub-command: %d", apf_subcmd);
688 		ret_val = -ENOTSUPP;
689 	}
690 
691 	qdf_spin_lock(&context->lock);
692 	context->cmd_in_progress = false;
693 	qdf_spin_unlock(&context->lock);
694 
695 	return ret_val;
696 }
697 
698 int
699 wlan_hdd_cfg80211_apf_offload(struct wiphy *wiphy, struct wireless_dev *wdev,
700 			      const void *data, int data_len)
701 {
702 	int errno;
703 	struct osif_vdev_sync *vdev_sync;
704 
705 	errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync);
706 	if (errno)
707 		return errno;
708 
709 	errno = __wlan_hdd_cfg80211_apf_offload(wiphy, wdev, data, data_len);
710 
711 	osif_vdev_sync_op_stop(vdev_sync);
712 
713 	return errno;
714 }
715 
716