1 /* 2 * Copyright (c) 2016-2018 The Linux Foundation. All rights reserved. 3 * Copyright (c) 2002-2006, Atheros Communications Inc. 4 * 5 * Permission to use, copy, modify, and/or distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 /** 19 * DOC: This file contains the dfs_attach() and dfs_detach() functions as well 20 * as the dfs_control() function which is used to process ioctls related to DFS. 21 * For Linux/Mac, "radartool" is the command line tool that can be used to call 22 * various ioctls to set and get radar detection thresholds. 23 */ 24 25 #include "../dfs_zero_cac.h" 26 #include "wlan_dfs_lmac_api.h" 27 #include "wlan_dfs_mlme_api.h" 28 #include "wlan_dfs_tgt_api.h" 29 #include "../dfs_internal.h" 30 #include "../dfs_filter_init.h" 31 #include "../dfs_full_offload.h" 32 #include "wlan_dfs_utils_api.h" 33 #include "../dfs_etsi_precac.h" 34 #include "../dfs_partial_offload_radar.h" 35 36 /** 37 * dfs_testtimer_task() - Sends CSA in the current channel. 38 * 39 * When the user sets usenol to 0 and inject the RADAR, AP does not mark the 40 * channel as RADAR and does not add the channel to NOL. It sends the CSA in 41 * the current channel. 42 */ 43 static os_timer_func(dfs_testtimer_task) 44 { 45 struct wlan_dfs *dfs = NULL; 46 47 OS_GET_TIMER_ARG(dfs, struct wlan_dfs *); 48 dfs->wlan_dfstest = 0; 49 50 /* 51 * Flip the channel back to the original channel. 52 * Make sure this is done properly with a CSA. 53 */ 54 dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS, "go back to channel %d", 55 dfs->wlan_dfstest_ieeechan); 56 dfs_mlme_start_csa(dfs->dfs_pdev_obj, 57 dfs->wlan_dfstest_ieeechan, 58 dfs->dfs_curchan->dfs_ch_freq, 59 dfs->dfs_curchan->dfs_ch_vhtop_ch_freq_seg2, 60 dfs->dfs_curchan->dfs_ch_flags); 61 } 62 63 int dfs_get_debug_info(struct wlan_dfs *dfs, void *data) 64 { 65 if (data) 66 *(uint32_t *)data = dfs->dfs_proc_phyerr; 67 68 return (int)dfs->dfs_proc_phyerr; 69 } 70 71 void dfs_main_task_testtimer_init(struct wlan_dfs *dfs) 72 { 73 qdf_timer_init(NULL, 74 &(dfs->wlan_dfstesttimer), 75 dfs_testtimer_task, (void *)dfs, 76 QDF_TIMER_TYPE_WAKE_APPS); 77 } 78 79 int dfs_create_object(struct wlan_dfs **dfs) 80 { 81 *dfs = (struct wlan_dfs *)qdf_mem_malloc(sizeof(**dfs)); 82 if (!(*dfs)) { 83 dfs_alert(*dfs, WLAN_DEBUG_DFS_ALWAYS, "wlan_dfs allocation failed"); 84 return 1; 85 } 86 87 qdf_mem_zero(*dfs, sizeof(**dfs)); 88 89 (*dfs)->dfs_curchan = (struct dfs_channel *)qdf_mem_malloc( 90 sizeof(struct dfs_channel)); 91 92 if (!((*dfs)->dfs_curchan)) { 93 dfs_alert(*dfs, WLAN_DEBUG_DFS_ALWAYS, "dfs_curchan allocation failed"); 94 return 1; 95 } 96 97 return 0; 98 } 99 100 int dfs_attach(struct wlan_dfs *dfs) 101 { 102 int ret; 103 104 if (!dfs->dfs_is_offload_enabled) { 105 ret = dfs_main_attach(dfs); 106 107 /* 108 * For full offload we have a wmi handler registered to process 109 * a radar event from firmware in the event of a radar detect. 110 * So, init of timer, dfs_task is not required for 111 * full-offload. dfs_task timer is called in 112 * dfs_main_timer_init within dfs_main_attach for 113 * partial-offload in the event of radar detect. 114 */ 115 if (ret) { 116 dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS, "dfs_main_attach failed"); 117 return ret; 118 } 119 } 120 dfs_cac_attach(dfs); 121 dfs_zero_cac_attach(dfs); 122 dfs_etsi_precac_attach(dfs); 123 dfs_nol_attach(dfs); 124 125 /* 126 * Init of timer ,dfs_testtimer_task is required by both partial 127 * and full offload, indicating test mode timer initialization for both. 128 */ 129 dfs_main_task_testtimer_init(dfs); 130 return 0; 131 } 132 133 void dfs_stop(struct wlan_dfs *dfs) 134 { 135 dfs_nol_timer_cleanup(dfs); 136 dfs_nol_workqueue_cleanup(dfs); 137 dfs_clear_nolhistory(dfs); 138 } 139 140 void dfs_task_testtimer_reset(struct wlan_dfs *dfs) 141 { 142 if (dfs->wlan_dfstest) { 143 qdf_timer_sync_cancel(&dfs->wlan_dfstesttimer); 144 dfs->wlan_dfstest = 0; 145 } 146 } 147 148 void dfs_task_testtimer_free(struct wlan_dfs *dfs) 149 { 150 qdf_timer_free(&dfs->wlan_dfstesttimer); 151 dfs->wlan_dfstest = 0; 152 } 153 154 void dfs_reset(struct wlan_dfs *dfs) 155 { 156 if (!dfs) { 157 dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS, "dfs is NULL"); 158 return; 159 } 160 161 dfs_cac_timer_reset(dfs); 162 dfs_zero_cac_reset(dfs); 163 if (!dfs->dfs_is_offload_enabled) { 164 dfs_main_timer_reset(dfs); 165 dfs_host_wait_timer_reset(dfs); 166 dfs->dfs_event_log_count = 0; 167 } 168 dfs_task_testtimer_reset(dfs); 169 } 170 171 void dfs_timer_free(struct wlan_dfs *dfs) 172 { 173 dfs_cac_timer_free(dfs); 174 dfs_zero_cac_timer_free(dfs); 175 176 if (!dfs->dfs_is_offload_enabled) { 177 dfs_main_timer_free(dfs); 178 dfs_host_wait_timer_free(dfs); 179 } 180 181 dfs_task_testtimer_free(dfs); 182 dfs_nol_timer_free(dfs); 183 } 184 185 void dfs_detach(struct wlan_dfs *dfs) 186 { 187 dfs_timer_free(dfs); 188 if (!dfs->dfs_is_offload_enabled) 189 dfs_main_detach(dfs); 190 dfs_etsi_precac_detach(dfs); 191 dfs_zero_cac_detach(dfs); 192 dfs_nol_detach(dfs); 193 } 194 195 void dfs_destroy_object(struct wlan_dfs *dfs) 196 { 197 qdf_mem_free(dfs->dfs_curchan); 198 qdf_mem_free(dfs); 199 } 200 201 int dfs_control(struct wlan_dfs *dfs, 202 u_int id, 203 void *indata, 204 uint32_t insize, 205 void *outdata, 206 uint32_t *outsize) 207 { 208 struct wlan_dfs_phyerr_param peout; 209 struct dfs_ioctl_params *dfsparams; 210 int error = 0; 211 uint32_t val = 0; 212 struct dfsreq_nolinfo *nol; 213 uint32_t *data = NULL; 214 int i; 215 struct dfs_emulate_bang_radar_test_cmd dfs_unit_test; 216 217 qdf_mem_zero(&dfs_unit_test, sizeof(dfs_unit_test)); 218 219 if (!dfs) { 220 dfs_err(NULL, WLAN_DEBUG_DFS_ALWAYS, "dfs is NULL"); 221 goto bad; 222 } 223 224 switch (id) { 225 case DFS_SET_THRESH: 226 if (insize < sizeof(struct dfs_ioctl_params) || !indata) { 227 dfs_debug(dfs, WLAN_DEBUG_DFS1, 228 "insize = %d, expected = %zu bytes, indata = %pK", 229 insize, 230 sizeof(struct dfs_ioctl_params), 231 indata); 232 error = -EINVAL; 233 break; 234 } 235 dfsparams = (struct dfs_ioctl_params *)indata; 236 if (!dfs_set_thresholds(dfs, DFS_PARAM_FIRPWR, 237 dfsparams->dfs_firpwr)) 238 error = -EINVAL; 239 if (!dfs_set_thresholds(dfs, DFS_PARAM_RRSSI, 240 dfsparams->dfs_rrssi)) 241 error = -EINVAL; 242 if (!dfs_set_thresholds(dfs, DFS_PARAM_HEIGHT, 243 dfsparams->dfs_height)) 244 error = -EINVAL; 245 if (!dfs_set_thresholds(dfs, DFS_PARAM_PRSSI, 246 dfsparams->dfs_prssi)) 247 error = -EINVAL; 248 if (!dfs_set_thresholds(dfs, DFS_PARAM_INBAND, 249 dfsparams->dfs_inband)) 250 error = -EINVAL; 251 252 /* 5413 speicfic. */ 253 if (!dfs_set_thresholds(dfs, DFS_PARAM_RELPWR, 254 dfsparams->dfs_relpwr)) 255 error = -EINVAL; 256 if (!dfs_set_thresholds(dfs, DFS_PARAM_RELSTEP, 257 dfsparams->dfs_relstep)) 258 error = -EINVAL; 259 if (!dfs_set_thresholds(dfs, DFS_PARAM_MAXLEN, 260 dfsparams->dfs_maxlen)) 261 error = -EINVAL; 262 break; 263 case DFS_GET_THRESH: 264 if (!outdata || !outsize || 265 *outsize < sizeof(struct dfs_ioctl_params)) { 266 error = -EINVAL; 267 break; 268 } 269 *outsize = sizeof(struct dfs_ioctl_params); 270 dfsparams = (struct dfs_ioctl_params *) outdata; 271 272 /* Fetch the DFS thresholds using the internal representation */ 273 (void) dfs_get_thresholds(dfs, &peout); 274 275 /* Convert them to the dfs IOCTL representation. */ 276 wlan_dfs_dfsparam_to_ioctlparam(&peout, dfsparams); 277 break; 278 case DFS_RADARDETECTS: 279 if (!outdata || !outsize || *outsize < sizeof(uint32_t)) { 280 error = -EINVAL; 281 break; 282 } 283 *outsize = sizeof(uint32_t); 284 *((uint32_t *)outdata) = dfs->wlan_dfs_stats.num_radar_detects; 285 break; 286 case DFS_DISABLE_DETECT: 287 dfs->dfs_proc_phyerr &= ~DFS_RADAR_EN; 288 dfs->dfs_proc_phyerr &= ~DFS_SECOND_SEGMENT_RADAR_EN; 289 dfs->dfs_ignore_dfs = 1; 290 dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS, 291 "enable detects, ignore_dfs %d", 292 dfs->dfs_ignore_dfs ? 1:0); 293 break; 294 case DFS_ENABLE_DETECT: 295 dfs->dfs_proc_phyerr |= DFS_RADAR_EN; 296 dfs->dfs_proc_phyerr |= DFS_SECOND_SEGMENT_RADAR_EN; 297 dfs->dfs_ignore_dfs = 0; 298 dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS 299 , "enable detects, ignore_dfs %d", 300 dfs->dfs_ignore_dfs ? 1:0); 301 break; 302 case DFS_DISABLE_FFT: 303 dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS, 304 "TODO disable FFT val=0x%x", val); 305 break; 306 case DFS_ENABLE_FFT: 307 dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS, 308 "TODO enable FFT val=0x%x", val); 309 break; 310 case DFS_SET_DEBUG_LEVEL: 311 if (insize < sizeof(uint32_t) || !indata) { 312 error = -EINVAL; 313 break; 314 } 315 dfs->dfs_debug_mask = *(uint32_t *)indata; 316 317 /* Do not allow user to set the ALWAYS/MAX bit. 318 * It will be used internally by dfs print macro(s) 319 * to print messages when dfs is NULL. 320 */ 321 dfs->dfs_debug_mask &= ~(WLAN_DEBUG_DFS_ALWAYS); 322 323 dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS, 324 "debug level now = 0x%x", dfs->dfs_debug_mask); 325 if (dfs->dfs_debug_mask & WLAN_DEBUG_DFS3) { 326 /* Enable debug Radar Event */ 327 dfs->dfs_event_log_on = 1; 328 } else if ((utils_get_dfsdomain(dfs->dfs_pdev_obj) == 329 DFS_FCC_DOMAIN) && 330 lmac_is_host_dfs_check_support_enabled(dfs->dfs_pdev_obj)) { 331 dfs->dfs_event_log_on = 1; 332 } else { 333 dfs->dfs_event_log_on = 0; 334 } 335 break; 336 case DFS_SET_FALSE_RSSI_THRES: 337 if (insize < sizeof(uint32_t) || !indata) { 338 error = -EINVAL; 339 break; 340 } 341 dfs->wlan_dfs_false_rssi_thres = *(uint32_t *)indata; 342 dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS, 343 "false RSSI threshold now = 0x%x", 344 dfs->wlan_dfs_false_rssi_thres); 345 break; 346 case DFS_SET_PEAK_MAG: 347 if (insize < sizeof(uint32_t) || !indata) { 348 error = -EINVAL; 349 break; 350 } 351 dfs->wlan_dfs_peak_mag = *(uint32_t *)indata; 352 dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS, 353 "peak_mag now = 0x%x", 354 dfs->wlan_dfs_peak_mag); 355 break; 356 case DFS_GET_CAC_VALID_TIME: 357 if (!outdata || !outsize || *outsize < sizeof(uint32_t)) { 358 error = -EINVAL; 359 break; 360 } 361 *outsize = sizeof(uint32_t); 362 *((uint32_t *)outdata) = dfs->dfs_cac_valid_time; 363 break; 364 case DFS_SET_CAC_VALID_TIME: 365 if (insize < sizeof(uint32_t) || !indata) { 366 error = -EINVAL; 367 break; 368 } 369 dfs->dfs_cac_valid_time = *(uint32_t *)indata; 370 dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS, 371 "dfs timeout = %d", dfs->dfs_cac_valid_time); 372 break; 373 case DFS_IGNORE_CAC: 374 if (insize < sizeof(uint32_t) || !indata) { 375 error = -EINVAL; 376 break; 377 } 378 379 if (*(uint32_t *)indata) 380 dfs->dfs_ignore_cac = 1; 381 else 382 dfs->dfs_ignore_cac = 0; 383 384 dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS, 385 "ignore cac = 0x%x", dfs->dfs_ignore_cac); 386 break; 387 case DFS_SET_NOL_TIMEOUT: 388 if (insize < sizeof(uint32_t) || !indata) { 389 error = -EINVAL; 390 break; 391 } 392 if (*(int *)indata) 393 dfs->wlan_dfs_nol_timeout = *(int *)indata; 394 else 395 dfs->wlan_dfs_nol_timeout = DFS_NOL_TIMEOUT_S; 396 397 dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS, "nol timeout = %d sec", 398 dfs->wlan_dfs_nol_timeout); 399 break; 400 case DFS_MUTE_TIME: 401 if (insize < sizeof(uint32_t) || !indata) { 402 error = -EINVAL; 403 break; 404 } 405 data = (uint32_t *) indata; 406 dfs->wlan_dfstesttime = *data; 407 dfs->wlan_dfstesttime *= (1000); /* convert sec into ms */ 408 break; 409 case DFS_GET_USENOL: 410 if (!outdata || !outsize || *outsize < sizeof(uint32_t)) { 411 error = -EINVAL; 412 break; 413 } 414 *outsize = sizeof(uint32_t); 415 *((uint32_t *)outdata) = dfs->dfs_use_nol; 416 417 dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS, 418 "#Phyerr=%d, #false detect=%d, #queued=%d", 419 dfs->dfs_phyerr_count, 420 dfs->dfs_phyerr_reject_count, 421 dfs->dfs_phyerr_queued_count); 422 423 dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS, 424 "dfs_phyerr_freq_min=%d, dfs_phyerr_freq_max=%d", 425 dfs->dfs_phyerr_freq_min, 426 dfs->dfs_phyerr_freq_max); 427 428 dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS, 429 "Total radar events detected=%d, entries in the radar queue follows:", 430 dfs->dfs_event_log_count); 431 432 for (i = 0; (i < DFS_EVENT_LOG_SIZE) && 433 (i < dfs->dfs_event_log_count); i++) { 434 #define FREQ_OFFSET1 ((int)dfs->radar_log[i].freq_offset_khz / 1000) 435 #define FREQ_OFFSET2 ((int)abs(dfs->radar_log[i].freq_offset_khz) % 1000) 436 dfs_debug(dfs, WLAN_DEBUG_DFS, 437 "ts=%llu diff_ts=%u rssi=%u dur=%u, is_chirp=%d, seg_id=%d, sidx=%d, freq_offset=%d.%dMHz, peak_mag=%d, total_gain=%d, mb_gain=%d, relpwr_db=%d, delta_diff=%d, delta_peak=%d, psidx_diff=%d\n", 438 dfs->radar_log[i].ts, 439 dfs->radar_log[i].diff_ts, 440 dfs->radar_log[i].rssi, 441 dfs->radar_log[i].dur, 442 dfs->radar_log[i].is_chirp, 443 dfs->radar_log[i].seg_id, 444 dfs->radar_log[i].sidx, 445 FREQ_OFFSET1, 446 FREQ_OFFSET2, 447 dfs->radar_log[i].peak_mag, 448 dfs->radar_log[i].total_gain, 449 dfs->radar_log[i].mb_gain, 450 dfs->radar_log[i].relpwr_db, 451 dfs->radar_log[i].delta_diff, 452 dfs->radar_log[i].delta_peak, 453 dfs->radar_log[i].psidx_diff); 454 } 455 dfs->dfs_event_log_count = 0; 456 dfs->dfs_phyerr_count = 0; 457 dfs->dfs_phyerr_reject_count = 0; 458 dfs->dfs_phyerr_queued_count = 0; 459 dfs->dfs_phyerr_freq_min = 0x7fffffff; 460 dfs->dfs_phyerr_freq_max = 0; 461 break; 462 case DFS_SET_USENOL: 463 if (insize < sizeof(uint32_t) || !indata) { 464 error = -EINVAL; 465 break; 466 } 467 dfs->dfs_use_nol = *(uint32_t *)indata; 468 break; 469 case DFS_GET_NOL: 470 if (!outdata || !outsize || 471 *outsize < sizeof(struct dfsreq_nolinfo)) { 472 error = -EINVAL; 473 break; 474 } 475 *outsize = sizeof(struct dfsreq_nolinfo); 476 nol = (struct dfsreq_nolinfo *)outdata; 477 DFS_GET_NOL_LOCKED(dfs, 478 (struct dfsreq_nolelem *)nol->dfs_nol, 479 &nol->dfs_ch_nchans); 480 DFS_PRINT_NOL_LOCKED(dfs); 481 break; 482 case DFS_SET_NOL: 483 if (insize < sizeof(struct dfsreq_nolinfo) || !indata) { 484 error = -EINVAL; 485 break; 486 } 487 nol = (struct dfsreq_nolinfo *) indata; 488 dfs_set_nol(dfs, 489 (struct dfsreq_nolelem *)nol->dfs_nol, 490 nol->dfs_ch_nchans); 491 break; 492 case DFS_SHOW_NOL: 493 DFS_PRINT_NOL_LOCKED(dfs); 494 break; 495 case DFS_SHOW_NOLHISTORY: 496 dfs_print_nolhistory(dfs); 497 break; 498 case DFS_BANGRADAR: 499 if (dfs->dfs_is_offload_enabled) { 500 error = dfs_fill_emulate_bang_radar_test(dfs, 501 SEG_ID_PRIMARY, 502 &dfs_unit_test); 503 } else { 504 dfs->dfs_bangradar = 1; 505 error = dfs_start_host_based_bangradar(dfs); 506 } 507 break; 508 case DFS_SHOW_PRECAC_LISTS: 509 dfs_print_precaclists(dfs); 510 dfs_print_etsi_precaclists(dfs); 511 break; 512 case DFS_RESET_PRECAC_LISTS: 513 dfs_reset_precac_lists(dfs); 514 dfs_reset_etsi_precac_lists(dfs); 515 break; 516 case DFS_SECOND_SEGMENT_BANGRADAR: 517 if (dfs->dfs_is_offload_enabled) { 518 error = dfs_fill_emulate_bang_radar_test(dfs, 519 SEG_ID_SECONDARY, 520 &dfs_unit_test); 521 } else { 522 dfs->dfs_second_segment_bangradar = 1; 523 error = dfs_start_host_based_bangradar(dfs); 524 } 525 break; 526 default: 527 error = -EINVAL; 528 } 529 530 bad: 531 return error; 532 } 533 534 void dfs_set_current_channel(struct wlan_dfs *dfs, 535 uint16_t dfs_ch_freq, 536 uint64_t dfs_ch_flags, 537 uint16_t dfs_ch_flagext, 538 uint8_t dfs_ch_ieee, 539 uint8_t dfs_ch_vhtop_ch_freq_seg1, 540 uint8_t dfs_ch_vhtop_ch_freq_seg2) 541 { 542 if (!dfs) { 543 dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS, "dfs is NULL"); 544 return; 545 } 546 547 dfs->dfs_curchan->dfs_ch_freq = dfs_ch_freq; 548 dfs->dfs_curchan->dfs_ch_flags = dfs_ch_flags; 549 dfs->dfs_curchan->dfs_ch_flagext = dfs_ch_flagext; 550 dfs->dfs_curchan->dfs_ch_ieee = dfs_ch_ieee; 551 dfs->dfs_curchan->dfs_ch_vhtop_ch_freq_seg1 = dfs_ch_vhtop_ch_freq_seg1; 552 dfs->dfs_curchan->dfs_ch_vhtop_ch_freq_seg2 = dfs_ch_vhtop_ch_freq_seg2; 553 } 554 555 void dfs_update_cur_chan_flags(struct wlan_dfs *dfs, 556 uint64_t flags, 557 uint16_t flagext) 558 { 559 dfs->dfs_curchan->dfs_ch_flags = flags; 560 dfs->dfs_curchan->dfs_ch_flagext = flagext; 561 } 562