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