1 /* 2 * Copyright (c) 2011,2017-2018 The Linux Foundation. All rights reserved. 3 * 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 #include "spectral_cmn_api_i.h" 21 #include "spectral_da_api_i.h" 22 #include "spectral_ol_api_i.h" 23 #include <qdf_mem.h> 24 #include <qdf_types.h> 25 #ifdef CONFIG_WIN 26 #include <osif_private.h> 27 #include <wlan_mlme_dispatcher.h> 28 #endif /*CONFIG_WIN*/ 29 #include <wlan_spectral_public_structs.h> 30 #include <wlan_cfg80211_spectral.h> 31 32 /** 33 * spectral_get_vdev() - Get pointer to vdev to be used for Spectral 34 * operations 35 * @pdev: Pointer to pdev 36 * 37 * Spectral operates on pdev. However, in order to retrieve some WLAN 38 * properties, a vdev is required. To facilitate this, the function returns the 39 * first vdev in our pdev. The caller should release the reference to the vdev 40 * once it is done using it. Additionally, the caller should ensure it has a 41 * reference to the pdev at the time of calling this function, and should 42 * release the pdev reference either after this function returns or at a later 43 * time when the caller is done using pdev. 44 * TODO: 45 * - If the framework later provides an API to obtain the first active 46 * vdev, then it would be preferable to use this API. 47 * - Use a common get_vdev() handler for core and target_if using Rx ops. This 48 * is deferred till details emerge on framework providing API to get first 49 * active vdev. 50 * 51 * Return: Pointer to vdev on success, NULL on failure 52 */ 53 static struct wlan_objmgr_vdev* 54 spectral_get_vdev(struct wlan_objmgr_pdev *pdev) 55 { 56 struct wlan_objmgr_vdev *vdev = NULL; 57 58 qdf_assert_always(pdev); 59 60 vdev = wlan_objmgr_get_vdev_by_id_from_pdev(pdev, 0, WLAN_SPECTRAL_ID); 61 62 if (!vdev) { 63 spectral_warn("Unable to get first vdev of pdev"); 64 return NULL; 65 } 66 67 return vdev; 68 } 69 70 #ifndef CONFIG_MCL 71 /** 72 * spectral_register_cfg80211_handlers() - Register spectral cfg80211 handlers 73 * @pdev: Pointer to pdev 74 * 75 * Register spectral cfg80211 handlers 76 * Handlers can be different for WIN and MCL 77 * 78 * Return: None 79 */ 80 static void 81 spectral_register_cfg80211_handlers(struct wlan_objmgr_pdev *pdev) 82 { 83 wlan_cfg80211_register_spectral_cmd_handler( 84 pdev, 85 SPECTRAL_SCAN_START_HANDLER_IDX, 86 wlan_cfg80211_spectral_scan_config_and_start); 87 wlan_cfg80211_register_spectral_cmd_handler( 88 pdev, 89 SPECTRAL_SCAN_STOP_HANDLER_IDX, 90 wlan_cfg80211_spectral_scan_stop); 91 wlan_cfg80211_register_spectral_cmd_handler( 92 pdev, 93 SPECTRAL_SCAN_GET_CONFIG_HANDLER_IDX, 94 wlan_cfg80211_spectral_scan_get_config); 95 wlan_cfg80211_register_spectral_cmd_handler( 96 pdev, 97 SPECTRAL_SCAN_GET_DIAG_STATS_HANDLER_IDX, 98 wlan_cfg80211_spectral_scan_get_diag_stats); 99 wlan_cfg80211_register_spectral_cmd_handler( 100 pdev, 101 SPECTRAL_SCAN_GET_CAP_HANDLER_IDX, 102 wlan_cfg80211_spectral_scan_get_cap); 103 wlan_cfg80211_register_spectral_cmd_handler( 104 pdev, 105 SPECTRAL_SCAN_GET_STATUS_HANDLER_IDX, 106 wlan_cfg80211_spectral_scan_get_status); 107 } 108 #else 109 static void 110 spectral_register_cfg80211_handlers(struct wlan_objmgr_pdev *pdev) 111 { 112 } 113 #endif 114 115 int 116 spectral_control_cmn(struct wlan_objmgr_pdev *pdev, 117 u_int id, 118 void *indata, 119 uint32_t insize, void *outdata, uint32_t *outsize) 120 { 121 int error = 0; 122 int temp_debug; 123 struct spectral_config sp_out; 124 struct spectral_config *sp_in; 125 struct spectral_config *spectralparams; 126 struct spectral_context *sc; 127 struct wlan_objmgr_vdev *vdev = NULL; 128 uint8_t vdev_rxchainmask = 0; 129 130 if (!pdev) { 131 spectral_err("PDEV is NULL!"); 132 error = -EINVAL; 133 goto bad; 134 } 135 sc = spectral_get_spectral_ctx_from_pdev(pdev); 136 if (!sc) { 137 spectral_err("atf context is NULL!"); 138 error = -EINVAL; 139 goto bad; 140 } 141 142 switch (id) { 143 case SPECTRAL_SET_CONFIG: 144 { 145 if (insize < sizeof(struct spectral_config) || 146 !indata) { 147 error = -EINVAL; 148 break; 149 } 150 sp_in = (struct spectral_config *)indata; 151 if (sp_in->ss_count != 152 SPECTRAL_PHYERR_PARAM_NOVAL) { 153 if (sc->sptrlc_set_spectral_config( 154 pdev, 155 SPECTRAL_PARAM_SCAN_COUNT, 156 sp_in->ss_count)) 157 error = -EINVAL; 158 } 159 160 if (sp_in->ss_fft_period != 161 SPECTRAL_PHYERR_PARAM_NOVAL) { 162 if (sc->sptrlc_set_spectral_config( 163 pdev, 164 SPECTRAL_PARAM_FFT_PERIOD, 165 sp_in->ss_fft_period)) 166 error = -EINVAL; 167 } 168 169 if (sp_in->ss_period != SPECTRAL_PHYERR_PARAM_NOVAL) { 170 if (sc->sptrlc_set_spectral_config( 171 pdev, 172 SPECTRAL_PARAM_SCAN_PERIOD, 173 sp_in->ss_period)) 174 error = -EINVAL; 175 } 176 177 if (sp_in->ss_short_report != 178 SPECTRAL_PHYERR_PARAM_NOVAL) { 179 if (sc->sptrlc_set_spectral_config( 180 pdev, 181 SPECTRAL_PARAM_SHORT_REPORT, 182 (uint32_t) 183 (sp_in->ss_short_report ? 1 : 0))) 184 error = -EINVAL; 185 } 186 187 if (sp_in->ss_spectral_pri != 188 SPECTRAL_PHYERR_PARAM_NOVAL) { 189 if (sc->sptrlc_set_spectral_config( 190 pdev, 191 SPECTRAL_PARAM_SPECT_PRI, 192 (uint32_t)(sp_in->ss_spectral_pri))) 193 error = -EINVAL; 194 } 195 196 if (sp_in->ss_fft_size != SPECTRAL_PHYERR_PARAM_NOVAL) { 197 if (sc->sptrlc_set_spectral_config( 198 pdev, 199 SPECTRAL_PARAM_FFT_SIZE, 200 sp_in->ss_fft_size)) 201 error = -EINVAL; 202 } 203 204 if (sp_in->ss_gc_ena != SPECTRAL_PHYERR_PARAM_NOVAL) { 205 if (sc->sptrlc_set_spectral_config( 206 pdev, 207 SPECTRAL_PARAM_GC_ENA, 208 sp_in->ss_gc_ena)) 209 error = -EINVAL; 210 } 211 212 if (sp_in->ss_restart_ena != 213 SPECTRAL_PHYERR_PARAM_NOVAL) { 214 if (sc->sptrlc_set_spectral_config( 215 pdev, 216 SPECTRAL_PARAM_RESTART_ENA, 217 sp_in->ss_restart_ena)) 218 error = -EINVAL; 219 } 220 221 if (sp_in->ss_noise_floor_ref != 222 SPECTRAL_PHYERR_PARAM_NOVAL) { 223 if (sc->sptrlc_set_spectral_config( 224 pdev, 225 SPECTRAL_PARAM_NOISE_FLOOR_REF, 226 sp_in->ss_noise_floor_ref)) 227 error = -EINVAL; 228 } 229 230 if (sp_in->ss_init_delay != 231 SPECTRAL_PHYERR_PARAM_NOVAL) { 232 if (sc->sptrlc_set_spectral_config( 233 pdev, 234 SPECTRAL_PARAM_INIT_DELAY, 235 sp_in->ss_init_delay)) 236 error = -EINVAL; 237 } 238 239 if (sp_in->ss_nb_tone_thr != 240 SPECTRAL_PHYERR_PARAM_NOVAL) { 241 if (sc->sptrlc_set_spectral_config( 242 pdev, 243 SPECTRAL_PARAM_NB_TONE_THR, 244 sp_in->ss_nb_tone_thr)) 245 error = -EINVAL; 246 } 247 248 if (sp_in->ss_str_bin_thr != 249 SPECTRAL_PHYERR_PARAM_NOVAL) { 250 if (sc->sptrlc_set_spectral_config( 251 pdev, 252 SPECTRAL_PARAM_STR_BIN_THR, 253 sp_in->ss_str_bin_thr)) 254 error = -EINVAL; 255 } 256 257 if (sp_in->ss_wb_rpt_mode != 258 SPECTRAL_PHYERR_PARAM_NOVAL) { 259 if (sc->sptrlc_set_spectral_config( 260 pdev, 261 SPECTRAL_PARAM_WB_RPT_MODE, 262 sp_in->ss_wb_rpt_mode)) 263 error = -EINVAL; 264 } 265 266 if (sp_in->ss_rssi_rpt_mode != 267 SPECTRAL_PHYERR_PARAM_NOVAL) { 268 if (sc->sptrlc_set_spectral_config( 269 pdev, 270 SPECTRAL_PARAM_RSSI_RPT_MODE, 271 sp_in->ss_rssi_rpt_mode)) 272 error = -EINVAL; 273 } 274 275 if (sp_in->ss_rssi_thr != SPECTRAL_PHYERR_PARAM_NOVAL) { 276 if (sc->sptrlc_set_spectral_config( 277 pdev, 278 SPECTRAL_PARAM_RSSI_THR, 279 sp_in->ss_rssi_thr)) 280 error = -EINVAL; 281 } 282 283 if (sp_in->ss_pwr_format != 284 SPECTRAL_PHYERR_PARAM_NOVAL) { 285 if (sc->sptrlc_set_spectral_config( 286 pdev, 287 SPECTRAL_PARAM_PWR_FORMAT, 288 sp_in->ss_pwr_format)) 289 error = -EINVAL; 290 } 291 292 if (sp_in->ss_rpt_mode != SPECTRAL_PHYERR_PARAM_NOVAL) { 293 if (sc->sptrlc_set_spectral_config( 294 pdev, 295 SPECTRAL_PARAM_RPT_MODE, 296 sp_in->ss_rpt_mode)) 297 error = -EINVAL; 298 } 299 300 if (sp_in->ss_bin_scale != 301 SPECTRAL_PHYERR_PARAM_NOVAL) { 302 if (sc->sptrlc_set_spectral_config( 303 pdev, 304 SPECTRAL_PARAM_BIN_SCALE, 305 sp_in->ss_bin_scale)) 306 error = -EINVAL; 307 } 308 309 if (sp_in->ss_dbm_adj != SPECTRAL_PHYERR_PARAM_NOVAL) { 310 if (sc->sptrlc_set_spectral_config( 311 pdev, 312 SPECTRAL_PARAM_DBM_ADJ, 313 sp_in->ss_dbm_adj)) 314 error = -EINVAL; 315 } 316 317 if (sp_in->ss_chn_mask != SPECTRAL_PHYERR_PARAM_NOVAL) { 318 /* 319 * Check if any of the inactive Rx antenna 320 * chains is set active in spectral chainmask 321 */ 322 vdev = spectral_get_vdev(pdev); 323 if (!vdev) { 324 error = -ENOENT; 325 break; 326 } 327 328 vdev_rxchainmask = 329 wlan_vdev_mlme_get_rxchainmask(vdev); 330 wlan_objmgr_vdev_release_ref(vdev, 331 WLAN_SPECTRAL_ID); 332 333 if (!(sp_in->ss_chn_mask & vdev_rxchainmask)) { 334 spectral_err("Invalid Spectral Chainmask - Inactive Rx antenna chain cannot be an active spectral chain"); 335 error = -EINVAL; 336 break; 337 } else if (sc->sptrlc_set_spectral_config( 338 pdev, 339 SPECTRAL_PARAM_CHN_MASK, 340 sp_in->ss_chn_mask)) { 341 error = -EINVAL; 342 } 343 } 344 } 345 break; 346 347 case SPECTRAL_GET_CONFIG: 348 { 349 if (!outdata || !outsize || 350 (*outsize < sizeof(struct spectral_config))) { 351 error = -EINVAL; 352 break; 353 } 354 *outsize = sizeof(struct spectral_config); 355 sc->sptrlc_get_spectral_config(pdev, &sp_out); 356 spectralparams = (struct spectral_config *)outdata; 357 spectralparams->ss_fft_period = sp_out.ss_fft_period; 358 spectralparams->ss_period = sp_out.ss_period; 359 spectralparams->ss_count = sp_out.ss_count; 360 spectralparams->ss_short_report = 361 sp_out.ss_short_report; 362 spectralparams->ss_spectral_pri = 363 sp_out.ss_spectral_pri; 364 spectralparams->ss_fft_size = sp_out.ss_fft_size; 365 spectralparams->ss_gc_ena = sp_out.ss_gc_ena; 366 spectralparams->ss_restart_ena = sp_out.ss_restart_ena; 367 spectralparams->ss_noise_floor_ref = 368 sp_out.ss_noise_floor_ref; 369 spectralparams->ss_init_delay = sp_out.ss_init_delay; 370 spectralparams->ss_nb_tone_thr = sp_out.ss_nb_tone_thr; 371 spectralparams->ss_str_bin_thr = sp_out.ss_str_bin_thr; 372 spectralparams->ss_wb_rpt_mode = sp_out.ss_wb_rpt_mode; 373 spectralparams->ss_rssi_rpt_mode = 374 sp_out.ss_rssi_rpt_mode; 375 spectralparams->ss_rssi_thr = sp_out.ss_rssi_thr; 376 spectralparams->ss_pwr_format = sp_out.ss_pwr_format; 377 spectralparams->ss_rpt_mode = sp_out.ss_rpt_mode; 378 spectralparams->ss_bin_scale = sp_out.ss_bin_scale; 379 spectralparams->ss_dbm_adj = sp_out.ss_dbm_adj; 380 spectralparams->ss_chn_mask = sp_out.ss_chn_mask; 381 } 382 break; 383 384 case SPECTRAL_IS_ACTIVE: 385 { 386 if (!outdata || !outsize || 387 *outsize < sizeof(uint32_t)) { 388 error = -EINVAL; 389 break; 390 } 391 *outsize = sizeof(uint32_t); 392 *((uint32_t *)outdata) = 393 (uint32_t)sc->sptrlc_is_spectral_active(pdev); 394 } 395 break; 396 397 case SPECTRAL_IS_ENABLED: 398 { 399 if (!outdata || !outsize || 400 *outsize < sizeof(uint32_t)) { 401 error = -EINVAL; 402 break; 403 } 404 *outsize = sizeof(uint32_t); 405 *((uint32_t *)outdata) = 406 (uint32_t)sc->sptrlc_is_spectral_enabled(pdev); 407 } 408 break; 409 410 case SPECTRAL_SET_DEBUG_LEVEL: 411 { 412 if (insize < sizeof(uint32_t) || !indata) { 413 error = -EINVAL; 414 break; 415 } 416 temp_debug = *(uint32_t *)indata; 417 sc->sptrlc_set_debug_level(pdev, temp_debug); 418 } 419 break; 420 421 case SPECTRAL_GET_DEBUG_LEVEL: 422 { 423 if (!outdata || !outsize || 424 *outsize < sizeof(uint32_t)) { 425 error = -EINVAL; 426 break; 427 } 428 *outsize = sizeof(uint32_t); 429 *((uint32_t *)outdata) = 430 (uint32_t)sc->sptrlc_get_debug_level(pdev); 431 } 432 break; 433 434 case SPECTRAL_ACTIVATE_SCAN: 435 { 436 sc->sptrlc_start_spectral_scan(pdev); 437 } 438 break; 439 440 case SPECTRAL_STOP_SCAN: 441 { 442 sc->sptrlc_stop_spectral_scan(pdev); 443 } 444 break; 445 446 case SPECTRAL_GET_CAPABILITY_INFO: 447 { 448 if (!outdata || !outsize || 449 *outsize < sizeof(struct spectral_caps)) { 450 error = -EINVAL; 451 break; 452 } 453 *outsize = sizeof(struct spectral_caps); 454 sc->sptrlc_get_spectral_capinfo(pdev, outdata); 455 } 456 break; 457 458 case SPECTRAL_GET_DIAG_STATS: 459 { 460 if (!outdata || !outsize || 461 (*outsize < sizeof(struct spectral_diag_stats))) { 462 error = -EINVAL; 463 break; 464 } 465 *outsize = sizeof(struct spectral_diag_stats); 466 sc->sptrlc_get_spectral_diagstats(pdev, outdata); 467 } 468 break; 469 470 case SPECTRAL_GET_CHAN_WIDTH: 471 { 472 uint32_t chan_width; 473 474 vdev = spectral_get_vdev(pdev); 475 if (!vdev) 476 return -ENOENT; 477 478 chan_width = spectral_vdev_get_ch_width(vdev); 479 wlan_objmgr_vdev_release_ref(vdev, WLAN_SPECTRAL_ID); 480 481 if (!outdata || !outsize || 482 *outsize < sizeof(chan_width)) { 483 error = -EINVAL; 484 break; 485 } 486 *outsize = sizeof(chan_width); 487 *((uint32_t *)outdata) = (uint32_t)chan_width; 488 } 489 break; 490 491 default: 492 error = -EINVAL; 493 break; 494 } 495 496 bad: 497 return error; 498 } 499 500 /** 501 * spectral_ctx_deinit() - De-initialize function pointers from spectral context 502 * @sc - Reference to spectral_context object 503 * 504 * Return: None 505 */ 506 static void 507 spectral_ctx_deinit(struct spectral_context *sc) 508 { 509 if (sc) { 510 sc->sptrlc_ucfg_phyerr_config = NULL; 511 sc->sptrlc_pdev_spectral_init = NULL; 512 sc->sptrlc_pdev_spectral_deinit = NULL; 513 sc->sptrlc_set_spectral_config = NULL; 514 sc->sptrlc_get_spectral_config = NULL; 515 sc->sptrlc_start_spectral_scan = NULL; 516 sc->sptrlc_stop_spectral_scan = NULL; 517 sc->sptrlc_is_spectral_active = NULL; 518 sc->sptrlc_is_spectral_enabled = NULL; 519 sc->sptrlc_set_debug_level = NULL; 520 sc->sptrlc_get_debug_level = NULL; 521 sc->sptrlc_get_spectral_capinfo = NULL; 522 sc->sptrlc_get_spectral_diagstats = NULL; 523 } 524 } 525 526 QDF_STATUS 527 wlan_spectral_psoc_obj_create_handler(struct wlan_objmgr_psoc *psoc, void *arg) 528 { 529 struct spectral_context *sc = NULL; 530 531 if (!psoc) { 532 spectral_err("PSOC is NULL"); 533 return QDF_STATUS_E_FAILURE; 534 } 535 sc = (struct spectral_context *) 536 qdf_mem_malloc(sizeof(struct spectral_context)); 537 if (!sc) { 538 spectral_err("Failed to allocate spectral_ctx object"); 539 return QDF_STATUS_E_NOMEM; 540 } 541 qdf_mem_zero(sc, sizeof(struct spectral_context)); 542 sc->psoc_obj = psoc; 543 if (wlan_objmgr_psoc_get_dev_type(psoc) == WLAN_DEV_OL) 544 spectral_ctx_init_ol(sc); 545 #ifdef CONFIG_WIN 546 else if (wlan_objmgr_psoc_get_dev_type(psoc) == WLAN_DEV_DA) 547 spectral_ctx_init_da(sc); 548 #endif 549 wlan_objmgr_psoc_component_obj_attach(psoc, WLAN_UMAC_COMP_SPECTRAL, 550 (void *)sc, QDF_STATUS_SUCCESS); 551 552 return QDF_STATUS_SUCCESS; 553 } 554 555 QDF_STATUS 556 wlan_spectral_psoc_obj_destroy_handler(struct wlan_objmgr_psoc *psoc, 557 void *arg) 558 { 559 struct spectral_context *sc = NULL; 560 561 if (!psoc) { 562 spectral_err("PSOC is NULL"); 563 return QDF_STATUS_E_FAILURE; 564 } 565 sc = wlan_objmgr_psoc_get_comp_private_obj(psoc, 566 WLAN_UMAC_COMP_SPECTRAL); 567 if (sc) { 568 wlan_objmgr_psoc_component_obj_detach(psoc, 569 WLAN_UMAC_COMP_SPECTRAL, 570 (void *)sc); 571 /* Deinitilise function pointers from spectral context */ 572 spectral_ctx_deinit(sc); 573 qdf_mem_free(sc); 574 } 575 576 return QDF_STATUS_SUCCESS; 577 } 578 579 QDF_STATUS 580 wlan_spectral_pdev_obj_create_handler(struct wlan_objmgr_pdev *pdev, void *arg) 581 { 582 struct pdev_spectral *ps = NULL; 583 struct spectral_context *sc = NULL; 584 void *target_handle = NULL; 585 586 if (!pdev) { 587 spectral_err("PDEV is NULL"); 588 return QDF_STATUS_E_FAILURE; 589 } 590 ps = (struct pdev_spectral *) 591 qdf_mem_malloc(sizeof(struct pdev_spectral)); 592 if (!ps) { 593 spectral_err("Failed to allocate pdev_spectral object"); 594 return QDF_STATUS_E_NOMEM; 595 } 596 sc = spectral_get_spectral_ctx_from_pdev(pdev); 597 if (!sc) { 598 spectral_err("Spectral context is NULL!"); 599 goto cleanup; 600 } 601 602 qdf_mem_zero(ps, sizeof(struct pdev_spectral)); 603 ps->psptrl_pdev = pdev; 604 605 spectral_register_cfg80211_handlers(pdev); 606 if (sc->sptrlc_pdev_spectral_init) { 607 target_handle = sc->sptrlc_pdev_spectral_init(pdev); 608 if (!target_handle) { 609 spectral_err("Spectral lmac object is NULL!"); 610 goto cleanup; 611 } 612 ps->psptrl_target_handle = target_handle; 613 } 614 wlan_objmgr_pdev_component_obj_attach(pdev, WLAN_UMAC_COMP_SPECTRAL, 615 (void *)ps, QDF_STATUS_SUCCESS); 616 617 return QDF_STATUS_SUCCESS; 618 cleanup: 619 qdf_mem_free(ps); 620 return QDF_STATUS_E_FAILURE; 621 } 622 623 QDF_STATUS 624 wlan_spectral_pdev_obj_destroy_handler(struct wlan_objmgr_pdev *pdev, 625 void *arg) 626 { 627 struct pdev_spectral *ps = NULL; 628 struct spectral_context *sc = NULL; 629 630 if (!pdev) { 631 spectral_err("PDEV is NULL"); 632 return QDF_STATUS_E_FAILURE; 633 } 634 sc = spectral_get_spectral_ctx_from_pdev(pdev); 635 if (!sc) { 636 spectral_err("Spectral context is NULL!"); 637 return QDF_STATUS_E_FAILURE; 638 } 639 ps = wlan_objmgr_pdev_get_comp_private_obj(pdev, 640 WLAN_UMAC_COMP_SPECTRAL); 641 if (ps) { 642 if (sc->sptrlc_pdev_spectral_deinit) 643 sc->sptrlc_pdev_spectral_deinit(pdev); 644 ps->psptrl_target_handle = NULL; 645 wlan_objmgr_pdev_component_obj_detach(pdev, 646 WLAN_UMAC_COMP_SPECTRAL, 647 (void *)ps); 648 qdf_mem_free(ps); 649 } 650 651 return QDF_STATUS_SUCCESS; 652 } 653