xref: /wlan-dirver/qca-wifi-host-cmn/umac/dfs/core/src/misc/dfs.c (revision a175314c51a4ce5cec2835cc8a8c7dc0c1810915)
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