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