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