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