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(¶ms);
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