xref: /wlan-dirver/qca-wifi-host-cmn/umac/dfs/core/src/misc/dfs.c (revision 97f44cd39e4ff816eaa1710279d28cf6b9e65ad9)
1 /*
2  * Copyright (c) 2016-2020 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_objmgr_vdev_obj.h>
33 #include "wlan_dfs_utils_api.h"
34 #include "../dfs_process_radar_found_ind.h"
35 #include "../dfs_partial_offload_radar.h"
36 
37 /* Disable NOL in FW. */
38 #define DISABLE_NOL_FW 0
39 
40 #ifndef WLAN_DFS_STATIC_MEM_ALLOC
41 /*
42  * dfs_alloc_wlan_dfs() - allocate wlan_dfs buffer
43  *
44  * Return: buffer, null on failure.
45  */
46 static inline struct wlan_dfs *dfs_alloc_wlan_dfs(void)
47 {
48 	return qdf_mem_malloc(sizeof(struct wlan_dfs));
49 }
50 
51 /*
52  * dfs_free_wlan_dfs() - Free wlan_dfs buffer
53  * @dfs: wlan_dfs buffer pointer
54  *
55  * Return: None
56  */
57 static inline void dfs_free_wlan_dfs(struct wlan_dfs *dfs)
58 {
59 	qdf_mem_free(dfs);
60 }
61 
62 /*
63  * dfs_alloc_dfs_curchan() - allocate dfs_channel buffer
64  *
65  * Return: buffer, null on failure.
66  */
67 static inline struct dfs_channel *dfs_alloc_dfs_curchan(void)
68 {
69 	return qdf_mem_malloc(sizeof(struct dfs_channel));
70 }
71 
72 static inline struct dfs_channel *dfs_alloc_dfs_prevchan(void)
73 {
74 	return qdf_mem_malloc(sizeof(struct dfs_channel));
75 }
76 
77 /*
78  * dfs_free_dfs_chan() - Free dfs_channel buffer
79  * @dfs_chan: dfs_channel buffer pointer
80  *
81  * Return: None
82  */
83 static inline void dfs_free_dfs_chan(struct dfs_channel *dfs_chan)
84 {
85 	qdf_mem_free(dfs_chan);
86 }
87 
88 #else
89 
90 /* Static buffers for DFS objects */
91 static struct wlan_dfs global_dfs;
92 static struct dfs_channel global_dfs_curchan;
93 static struct dfs_channel global_dfs_prevchan;
94 
95 static inline struct wlan_dfs *dfs_alloc_wlan_dfs(void)
96 {
97 	return &global_dfs;
98 }
99 
100 static inline void dfs_free_wlan_dfs(struct wlan_dfs *dfs)
101 {
102 }
103 
104 static inline struct dfs_channel *dfs_alloc_dfs_curchan(void)
105 {
106 	return &global_dfs_curchan;
107 }
108 
109 static inline struct dfs_channel *dfs_alloc_dfs_prevchan(void)
110 {
111 	return &global_dfs_prevchan;
112 }
113 
114 static inline void dfs_free_dfs_chan(struct dfs_channel *dfs_chan)
115 {
116 }
117 #endif
118 
119 /**
120  * dfs_testtimer_task() - Sends CSA in the current channel.
121  *
122  * When the user sets usenol to 0 and inject the RADAR, AP does not mark the
123  * channel as RADAR and does not add the channel to NOL. It sends the CSA in
124  * the current channel.
125  */
126 #ifdef CONFIG_CHAN_FREQ_API
127 static os_timer_func(dfs_testtimer_task)
128 {
129 	struct wlan_dfs *dfs = NULL;
130 
131 	OS_GET_TIMER_ARG(dfs, struct wlan_dfs *);
132 	dfs->wlan_dfstest = 0;
133 
134 	/*
135 	 * Flip the channel back to the original channel.
136 	 * Make sure this is done properly with a CSA.
137 	 */
138 	dfs_debug(dfs, WLAN_DEBUG_DFS_ALWAYS, "go back to channel %d",
139 		  dfs->wlan_dfstest_ieeechan);
140 	dfs_mlme_start_csa_for_freq(dfs->dfs_pdev_obj,
141 				    dfs->wlan_dfstest_ieeechan,
142 				    dfs->dfs_curchan->dfs_ch_freq,
143 				    dfs->dfs_curchan->dfs_ch_mhz_freq_seg2,
144 				    dfs->dfs_curchan->dfs_ch_flags);
145 }
146 #else
147 #ifdef CONFIG_CHAN_NUM_API
148 static os_timer_func(dfs_testtimer_task)
149 {
150 	struct wlan_dfs *dfs = NULL;
151 
152 	OS_GET_TIMER_ARG(dfs, struct wlan_dfs *);
153 	dfs->wlan_dfstest = 0;
154 
155 	/*
156 	 * Flip the channel back to the original channel.
157 	 * Make sure this is done properly with a CSA.
158 	 */
159 	dfs_debug(dfs, WLAN_DEBUG_DFS_ALWAYS, "go back to channel %d",
160 		  dfs->wlan_dfstest_ieeechan);
161 	dfs_mlme_start_csa(dfs->dfs_pdev_obj,
162 			dfs->wlan_dfstest_ieeechan,
163 			dfs->dfs_curchan->dfs_ch_freq,
164 			dfs->dfs_curchan->dfs_ch_vhtop_ch_freq_seg2,
165 			dfs->dfs_curchan->dfs_ch_flags);
166 }
167 #endif
168 #endif
169 
170 int dfs_get_debug_info(struct wlan_dfs *dfs, void *data)
171 {
172 	if (data)
173 		*(uint32_t *)data = dfs->dfs_proc_phyerr;
174 
175 	return (int)dfs->dfs_proc_phyerr;
176 }
177 
178 void dfs_main_task_testtimer_init(struct wlan_dfs *dfs)
179 {
180 	qdf_timer_init(NULL,
181 		&(dfs->wlan_dfstesttimer),
182 		dfs_testtimer_task, (void *)dfs,
183 		QDF_TIMER_TYPE_WAKE_APPS);
184 }
185 
186 int dfs_create_object(struct wlan_dfs **dfs)
187 {
188 	*dfs = dfs_alloc_wlan_dfs();
189 	if (!(*dfs))
190 		return 1;
191 
192 	qdf_mem_zero(*dfs, sizeof(**dfs));
193 
194 	(*dfs)->dfs_curchan = dfs_alloc_dfs_curchan();
195 	if (!((*dfs)->dfs_curchan)) {
196 		dfs_free_wlan_dfs(*dfs);
197 		return 1;
198 	}
199 
200 	(*dfs)->dfs_prevchan = dfs_alloc_dfs_prevchan();
201 	if (!((*dfs)->dfs_prevchan)) {
202 		dfs_free_wlan_dfs(*dfs);
203 		return 1;
204 	}
205 	qdf_mem_zero((*dfs)->dfs_prevchan, sizeof(struct dfs_channel));
206 	return 0;
207 }
208 
209 #if defined(QCA_SUPPORT_DFS_CHAN_POSTNOL)
210 static void dfs_postnol_attach(struct wlan_dfs *dfs)
211 {
212 	dfs->dfs_chan_postnol_mode = CH_WIDTH_INVALID;
213 }
214 #else
215 static inline void dfs_postnol_attach(struct wlan_dfs *dfs)
216 {
217 }
218 #endif
219 
220 int dfs_attach(struct wlan_dfs *dfs)
221 {
222 	int ret;
223 
224 	if (!dfs->dfs_is_offload_enabled) {
225 		ret = dfs_main_attach(dfs);
226 
227 		/*
228 		 * For full offload we have a wmi handler registered to process
229 		 * a radar event from firmware in the event of a radar detect.
230 		 * So, init of timer, dfs_task is not required for
231 		 * full-offload. dfs_task timer is called in
232 		 * dfs_main_timer_init within dfs_main_attach for
233 		 * partial-offload in the event of radar detect.
234 		 */
235 		if (ret) {
236 			dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS,  "dfs_main_attach failed");
237 			return ret;
238 		}
239 	}
240 	dfs_cac_timer_attach(dfs);
241 	dfs_zero_cac_attach(dfs);
242 	dfs_nol_attach(dfs);
243 	dfs_postnol_attach(dfs);
244 
245 	/*
246 	 * Init of timer ,dfs_testtimer_task is required by both partial
247 	 * and full offload, indicating test mode timer initialization for both.
248 	 */
249 	dfs_main_task_testtimer_init(dfs);
250 	return 0;
251 }
252 
253 void dfs_stop(struct wlan_dfs *dfs)
254 {
255 	dfs_nol_timer_cleanup(dfs);
256 	dfs_nol_workqueue_cleanup(dfs);
257 	dfs_clear_nolhistory(dfs);
258 }
259 
260 void dfs_task_testtimer_reset(struct wlan_dfs *dfs)
261 {
262 	if (dfs->wlan_dfstest) {
263 		qdf_timer_sync_cancel(&dfs->wlan_dfstesttimer);
264 		dfs->wlan_dfstest = 0;
265 	}
266 }
267 
268 void dfs_task_testtimer_detach(struct wlan_dfs *dfs)
269 {
270 	qdf_timer_free(&dfs->wlan_dfstesttimer);
271 	dfs->wlan_dfstest = 0;
272 }
273 
274 void dfs_reset(struct wlan_dfs *dfs)
275 {
276 	if (!dfs) {
277 		dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS,  "dfs is NULL");
278 		return;
279 	}
280 
281 	dfs_cac_timer_reset(dfs);
282 	dfs_zero_cac_reset(dfs);
283 	if (!dfs->dfs_is_offload_enabled) {
284 		dfs_main_timer_reset(dfs);
285 		dfs_host_wait_timer_reset(dfs);
286 		dfs_false_radarfound_reset_vars(dfs);
287 	}
288 	dfs_task_testtimer_reset(dfs);
289 }
290 
291 void dfs_timer_detach(struct wlan_dfs *dfs)
292 {
293 	dfs_cac_timer_detach(dfs);
294 	dfs_zero_cac_timer_detach(dfs->dfs_soc_obj);
295 
296 	if (!dfs->dfs_is_offload_enabled) {
297 		dfs_main_timer_detach(dfs);
298 		dfs_host_wait_timer_detach(dfs);
299 	}
300 
301 	dfs_task_testtimer_detach(dfs);
302 	dfs_nol_timer_detach(dfs);
303 }
304 
305 void dfs_detach(struct wlan_dfs *dfs)
306 {
307 	dfs_timer_detach(dfs);
308 	if (!dfs->dfs_is_offload_enabled)
309 		dfs_main_detach(dfs);
310 	dfs_zero_cac_detach(dfs);
311 	dfs_nol_detach(dfs);
312 }
313 
314 #ifndef WLAN_DFS_STATIC_MEM_ALLOC
315 void dfs_destroy_object(struct wlan_dfs *dfs)
316 {
317 	dfs_free_dfs_chan(dfs->dfs_prevchan);
318 	dfs_free_dfs_chan(dfs->dfs_curchan);
319 	dfs_free_wlan_dfs(dfs);
320 }
321 #else
322 void dfs_destroy_object(struct wlan_dfs *dfs)
323 {
324 }
325 #endif
326 
327 /* dfs_set_disable_radar_marking()- Set the flag to mark/unmark a radar flag
328  * on NOL channel.
329  * @dfs: Pointer to wlan_dfs structure.
330  * @disable_radar_marking: Flag to enable/disable marking channel as radar.
331  */
332 #if defined(WLAN_DFS_FULL_OFFLOAD) && defined(QCA_DFS_NOL_OFFLOAD)
333 static void dfs_set_disable_radar_marking(struct wlan_dfs *dfs,
334 					  bool disable_radar_marking)
335 {
336 	dfs->dfs_disable_radar_marking = disable_radar_marking;
337 }
338 #else
339 static inline void dfs_set_disable_radar_marking(struct wlan_dfs *dfs,
340 						 bool disable_radar_marking)
341 {
342 }
343 #endif
344 
345 #if defined(WLAN_DFS_FULL_OFFLOAD) && defined(QCA_DFS_NOL_OFFLOAD)
346 bool dfs_get_disable_radar_marking(struct wlan_dfs *dfs)
347 {
348 	return dfs->dfs_disable_radar_marking;
349 }
350 #else
351 static inline bool dfs_get_disable_radar_marking(struct wlan_dfs *dfs)
352 {
353 	return QDF_STATUS_SUCCESS;
354 }
355 #endif
356 
357 static QDF_STATUS
358 dfs_check_bangradar_sanity(struct wlan_dfs *dfs,
359 			   struct dfs_bangradar_params *bangradar_params)
360 {
361 	if (!bangradar_params) {
362 		dfs_debug(dfs, WLAN_DEBUG_DFS_ALWAYS,
363 			  "bangradar params is NULL");
364 		return -EINVAL;
365 	}
366 	if (dfs_is_true_160mhz_supported(dfs)) {
367 		if (abs(bangradar_params->freq_offset) >
368 		    FREQ_OFFSET_BOUNDARY_FOR_160MHZ) {
369 			dfs_debug(dfs, WLAN_DEBUG_DFS_ALWAYS,
370 				  "Frequency Offset out of bound");
371 			return -EINVAL;
372 		}
373 	} else if (abs(bangradar_params->freq_offset) >
374 		   FREQ_OFFSET_BOUNDARY_FOR_80MHZ) {
375 		dfs_debug(dfs, WLAN_DEBUG_DFS_ALWAYS,
376 			  "Frequency Offset out of bound");
377 		return -EINVAL;
378 	}
379 	if (bangradar_params->seg_id > SEG_ID_SECONDARY) {
380 		dfs_debug(dfs, WLAN_DEBUG_DFS_ALWAYS, "Invalid segment ID");
381 		return -EINVAL;
382 	}
383 	if ((bangradar_params->detector_id > dfs_get_agile_detector_id(dfs)) ||
384 	    ((bangradar_params->detector_id ==
385 	      dfs_get_agile_detector_id(dfs)) &&
386 	      !dfs->dfs_is_offload_enabled)) {
387 		dfs_debug(dfs, WLAN_DEBUG_DFS_ALWAYS, "Invalid detector ID");
388 		return -EINVAL;
389 	}
390 	return QDF_STATUS_SUCCESS;
391 }
392 
393 int dfs_control(struct wlan_dfs *dfs,
394 		u_int id,
395 		void *indata,
396 		uint32_t insize,
397 		void *outdata,
398 		uint32_t *outsize)
399 {
400 	struct wlan_dfs_phyerr_param peout;
401 	struct dfs_ioctl_params *dfsparams;
402 	struct dfs_bangradar_params *bangradar_params;
403 	int error = 0;
404 	uint32_t val = 0;
405 	struct dfsreq_nolinfo *nol;
406 	uint32_t *data = NULL;
407 	int i;
408 	int usenol_pdev_param;
409 
410 	if (!dfs) {
411 		dfs_err(NULL, WLAN_DEBUG_DFS_ALWAYS,  "dfs is NULL");
412 		goto bad;
413 	}
414 
415 	switch (id) {
416 	case DFS_SET_THRESH:
417 		if (insize < sizeof(struct dfs_ioctl_params) || !indata) {
418 			dfs_debug(dfs, WLAN_DEBUG_DFS1,
419 					"insize = %d, expected = %zu bytes, indata = %pK",
420 					insize,
421 					sizeof(struct dfs_ioctl_params),
422 					indata);
423 			error = -EINVAL;
424 			break;
425 		}
426 		dfsparams = (struct dfs_ioctl_params *)indata;
427 		if (!dfs_set_thresholds(dfs, DFS_PARAM_FIRPWR,
428 					dfsparams->dfs_firpwr))
429 			error = -EINVAL;
430 		if (!dfs_set_thresholds(dfs, DFS_PARAM_RRSSI,
431 					dfsparams->dfs_rrssi))
432 			error = -EINVAL;
433 		if (!dfs_set_thresholds(dfs, DFS_PARAM_HEIGHT,
434 					dfsparams->dfs_height))
435 			error = -EINVAL;
436 		if (!dfs_set_thresholds(dfs, DFS_PARAM_PRSSI,
437 					dfsparams->dfs_prssi))
438 			error = -EINVAL;
439 		if (!dfs_set_thresholds(dfs, DFS_PARAM_INBAND,
440 					dfsparams->dfs_inband))
441 			error = -EINVAL;
442 
443 		/* 5413 speicfic. */
444 		if (!dfs_set_thresholds(dfs, DFS_PARAM_RELPWR,
445 					dfsparams->dfs_relpwr))
446 			error = -EINVAL;
447 		if (!dfs_set_thresholds(dfs, DFS_PARAM_RELSTEP,
448 					dfsparams->dfs_relstep))
449 			error = -EINVAL;
450 		if (!dfs_set_thresholds(dfs, DFS_PARAM_MAXLEN,
451 					dfsparams->dfs_maxlen))
452 			error = -EINVAL;
453 		break;
454 	case DFS_BANGRADAR:
455 		/*
456 		 * Handle all types of Bangradar here.
457 		 * Bangradar arguments:
458 		 * seg_id      : Segment ID where radar should be injected.
459 		 * is_chirp    : Is chirp radar or non chirp radar.
460 		 * freq_offset : Frequency offset from center frequency.
461 		 *
462 		 * Type 1 (DFS_BANGRADAR_FOR_ALL_SUBCHANS): To add all subchans.
463 		 * Type 2 (DFS_BANGRADAR_FOR_ALL_SUBCHANS_OF_SEGID): To add all
464 		 *               subchans of given segment_id.
465 		 * Type 3 (DFS_BANGRADAR_FOR_SPECIFIC_SUBCHANS): To add specific
466 		 *               subchans based on the arguments.
467 		 *
468 		 * The arguments will already be filled in the indata structure
469 		 * based on the type.
470 		 * If an argument is not specified by user, it will be set to
471 		 * default (0) in the indata already and correspondingly,
472 		 * the type will change.
473 		 */
474 		if (insize < sizeof(struct dfs_bangradar_params) ||
475 		    !indata) {
476 			dfs_debug(dfs, WLAN_DEBUG_DFS1,
477 				  "insize = %d, expected = %zu bytes, indata = %pK",
478 				  insize,
479 				  sizeof(struct dfs_bangradar_params),
480 				  indata);
481 			error = -EINVAL;
482 			break;
483 		}
484 		bangradar_params = (struct dfs_bangradar_params *)indata;
485 		error = dfs_check_bangradar_sanity(dfs, bangradar_params);
486 		if (error != QDF_STATUS_SUCCESS)
487 			break;
488 		dfs->dfs_bangradar_type = bangradar_params->bangradar_type;
489 		dfs->dfs_seg_id = bangradar_params->seg_id;
490 		dfs->dfs_is_chirp = bangradar_params->is_chirp;
491 		dfs->dfs_freq_offset = bangradar_params->freq_offset;
492 
493 		if (dfs->dfs_is_offload_enabled) {
494 			error = dfs_fill_emulate_bang_radar_test(
495 					dfs,
496 					bangradar_params);
497 		} else {
498 			error = dfs_start_host_based_bangradar(dfs);
499 		}
500 		break;
501 	case DFS_GET_THRESH:
502 		if (!outdata || !outsize ||
503 				*outsize < sizeof(struct dfs_ioctl_params)) {
504 			error = -EINVAL;
505 			break;
506 		}
507 		*outsize = sizeof(struct dfs_ioctl_params);
508 		dfsparams = (struct dfs_ioctl_params *) outdata;
509 
510 		qdf_mem_zero(&peout, sizeof(struct wlan_dfs_phyerr_param));
511 
512 		/* Fetch the DFS thresholds using the internal representation */
513 		(void) dfs_get_thresholds(dfs, &peout);
514 
515 		/* Convert them to the dfs IOCTL representation. */
516 		wlan_dfs_dfsparam_to_ioctlparam(&peout, dfsparams);
517 		break;
518 	case DFS_RADARDETECTS:
519 		if (!outdata || !outsize || *outsize < sizeof(uint32_t)) {
520 			error = -EINVAL;
521 			break;
522 		}
523 		*outsize = sizeof(uint32_t);
524 		*((uint32_t *)outdata) = dfs->wlan_dfs_stats.num_radar_detects;
525 		break;
526 	case DFS_DISABLE_DETECT:
527 		dfs->dfs_proc_phyerr &= ~DFS_RADAR_EN;
528 		dfs->dfs_proc_phyerr &= ~DFS_SECOND_SEGMENT_RADAR_EN;
529 		dfs->dfs_ignore_dfs = 1;
530 		dfs_debug(dfs, WLAN_DEBUG_DFS_ALWAYS,
531 			  "enable detects, ignore_dfs %d",
532 			  dfs->dfs_ignore_dfs ? 1 : 0);
533 		break;
534 	case DFS_ENABLE_DETECT:
535 		dfs->dfs_proc_phyerr |= DFS_RADAR_EN;
536 		dfs->dfs_proc_phyerr |= DFS_SECOND_SEGMENT_RADAR_EN;
537 		dfs->dfs_ignore_dfs = 0;
538 		dfs_debug(dfs, WLAN_DEBUG_DFS_ALWAYS,
539 			  "enable detects, ignore_dfs %d",
540 			  dfs->dfs_ignore_dfs ? 1 : 0);
541 		break;
542 	case DFS_DISABLE_FFT:
543 		dfs_debug(dfs, WLAN_DEBUG_DFS_ALWAYS,
544 			  "TODO disable FFT val=0x%x", val);
545 		break;
546 	case DFS_ENABLE_FFT:
547 		dfs_debug(dfs, WLAN_DEBUG_DFS_ALWAYS,
548 			  "TODO enable FFT val=0x%x", val);
549 		break;
550 	case DFS_SET_DEBUG_LEVEL:
551 		if (insize < sizeof(uint32_t) || !indata) {
552 			error = -EINVAL;
553 			break;
554 		}
555 		dfs->dfs_debug_mask = *(uint32_t *)indata;
556 
557 		/* Do not allow user to set the ALWAYS/MAX bit.
558 		 * It will be used internally  by dfs print macro(s)
559 		 * to print messages when dfs is NULL.
560 		 */
561 		dfs->dfs_debug_mask &= ~(WLAN_DEBUG_DFS_ALWAYS);
562 
563 		dfs_debug(dfs, WLAN_DEBUG_DFS_ALWAYS,
564 			  "debug level now = 0x%x", dfs->dfs_debug_mask);
565 		if (dfs->dfs_debug_mask & WLAN_DEBUG_DFS3) {
566 			/* Enable debug Radar Event */
567 			dfs->dfs_event_log_on = 1;
568 		} else if ((utils_get_dfsdomain(dfs->dfs_pdev_obj) ==
569 		    DFS_FCC_DOMAIN) &&
570 		    lmac_is_host_dfs_check_support_enabled(dfs->dfs_pdev_obj)) {
571 			dfs->dfs_event_log_on = 1;
572 		} else {
573 			dfs->dfs_event_log_on = 0;
574 		}
575 		break;
576 	case DFS_SET_FALSE_RSSI_THRES:
577 		if (insize < sizeof(uint32_t) || !indata) {
578 			error = -EINVAL;
579 			break;
580 		}
581 		dfs->wlan_dfs_false_rssi_thres = *(uint32_t *)indata;
582 		dfs_debug(dfs, WLAN_DEBUG_DFS_ALWAYS,
583 			  "false RSSI threshold now = 0x%x",
584 			  dfs->wlan_dfs_false_rssi_thres);
585 		break;
586 	case DFS_SET_PEAK_MAG:
587 		if (insize < sizeof(uint32_t) || !indata) {
588 			error = -EINVAL;
589 			break;
590 		}
591 		dfs->wlan_dfs_peak_mag = *(uint32_t *)indata;
592 		dfs_debug(dfs, WLAN_DEBUG_DFS_ALWAYS,
593 			  "peak_mag now = 0x%x",
594 				dfs->wlan_dfs_peak_mag);
595 		break;
596 	case DFS_GET_CAC_VALID_TIME:
597 		if (!outdata || !outsize || *outsize < sizeof(uint32_t)) {
598 			error = -EINVAL;
599 			break;
600 		}
601 		*outsize = sizeof(uint32_t);
602 		*((uint32_t *)outdata) = dfs->dfs_cac_valid_time;
603 		break;
604 	case DFS_SET_CAC_VALID_TIME:
605 		if (insize < sizeof(uint32_t) || !indata) {
606 			error = -EINVAL;
607 			break;
608 		}
609 		dfs->dfs_cac_valid_time = *(uint32_t *)indata;
610 		dfs_debug(dfs, WLAN_DEBUG_DFS_ALWAYS,
611 			  "dfs timeout = %d", dfs->dfs_cac_valid_time);
612 		break;
613 	case DFS_IGNORE_CAC:
614 		if (insize < sizeof(uint32_t) || !indata) {
615 			error = -EINVAL;
616 			break;
617 		}
618 
619 		if (*(uint32_t *)indata)
620 			dfs->dfs_ignore_cac = 1;
621 		else
622 			dfs->dfs_ignore_cac = 0;
623 
624 		dfs_debug(dfs, WLAN_DEBUG_DFS_ALWAYS,
625 			  "ignore cac = 0x%x", dfs->dfs_ignore_cac);
626 		break;
627 	case DFS_SET_NOL_TIMEOUT:
628 		if (insize < sizeof(uint32_t) || !indata) {
629 			error = -EINVAL;
630 			break;
631 		}
632 		if (*(int *)indata)
633 			dfs->wlan_dfs_nol_timeout = *(int *)indata;
634 		else
635 			dfs->wlan_dfs_nol_timeout = DFS_NOL_TIMEOUT_S;
636 
637 		dfs_debug(dfs, WLAN_DEBUG_DFS_ALWAYS, "nol timeout = %d sec",
638 			  dfs->wlan_dfs_nol_timeout);
639 		break;
640 	case DFS_MUTE_TIME:
641 		if (insize < sizeof(uint32_t) || !indata) {
642 			error = -EINVAL;
643 			break;
644 		}
645 		data = (uint32_t *) indata;
646 		dfs->wlan_dfstesttime = *data;
647 		dfs->wlan_dfstesttime *= (1000); /* convert sec into ms */
648 		break;
649 	case DFS_GET_USENOL:
650 		if (!outdata || !outsize || *outsize < sizeof(uint32_t)) {
651 			error = -EINVAL;
652 			break;
653 		}
654 		*outsize = sizeof(uint32_t);
655 		*((uint32_t *)outdata) = dfs->dfs_use_nol;
656 
657 		dfs_debug(dfs, WLAN_DEBUG_DFS_ALWAYS,
658 			  "#Phyerr=%d, #false detect=%d, #queued=%d",
659 			  dfs->dfs_phyerr_count,
660 			  dfs->dfs_phyerr_reject_count,
661 			  dfs->dfs_phyerr_queued_count);
662 
663 		dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS,
664 			 "dfs_phyerr_freq_min=%d, dfs_phyerr_freq_max=%d",
665 			 dfs->dfs_phyerr_freq_min,
666 			 dfs->dfs_phyerr_freq_max);
667 
668 		dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS,
669 			 "Total radar events detected=%d, entries in the radar queue follows:",
670 			 dfs->dfs_event_log_count);
671 
672 		for (i = 0; (i < DFS_EVENT_LOG_SIZE) &&
673 				(i < dfs->dfs_event_log_count); i++) {
674 #define FREQ_OFFSET1 ((int)dfs->radar_log[i].freq_offset_khz / 1000)
675 #define FREQ_OFFSET2 ((int)abs(dfs->radar_log[i].freq_offset_khz) % 1000)
676 			dfs_debug(dfs, WLAN_DEBUG_DFS,
677 				  "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",
678 				  dfs->radar_log[i].ts,
679 				  dfs->radar_log[i].diff_ts,
680 				  dfs->radar_log[i].rssi,
681 				  dfs->radar_log[i].dur,
682 				  dfs->radar_log[i].is_chirp,
683 				  dfs->radar_log[i].seg_id,
684 				  dfs->radar_log[i].sidx,
685 				  FREQ_OFFSET1,
686 				  FREQ_OFFSET2,
687 				  dfs->radar_log[i].peak_mag,
688 				  dfs->radar_log[i].total_gain,
689 				  dfs->radar_log[i].mb_gain,
690 				  dfs->radar_log[i].relpwr_db,
691 				  dfs->radar_log[i].delta_diff,
692 				  dfs->radar_log[i].delta_peak,
693 				  dfs->radar_log[i].psidx_diff);
694 		}
695 		dfs->dfs_event_log_count = 0;
696 		dfs->dfs_phyerr_count = 0;
697 		dfs->dfs_phyerr_reject_count = 0;
698 		dfs->dfs_phyerr_queued_count = 0;
699 		dfs->dfs_phyerr_freq_min = 0x7fffffff;
700 		dfs->dfs_phyerr_freq_max = 0;
701 		break;
702 	case DFS_SET_USENOL:
703 		if (insize < sizeof(uint32_t) || !indata) {
704 			error = -EINVAL;
705 			break;
706 		}
707 		dfs->dfs_use_nol = *(uint32_t *)indata;
708 		usenol_pdev_param = dfs->dfs_use_nol;
709 		if (dfs->dfs_is_offload_enabled) {
710 			if (dfs->dfs_use_nol ==
711 				USENOL_ENABLE_NOL_HOST_DISABLE_NOL_FW)
712 				usenol_pdev_param = DISABLE_NOL_FW;
713 			tgt_dfs_send_usenol_pdev_param(dfs->dfs_pdev_obj,
714 						       usenol_pdev_param);
715 		}
716 		break;
717 	case DFS_SET_DISABLE_RADAR_MARKING:
718 		if (dfs->dfs_is_offload_enabled &&
719 		    (utils_get_dfsdomain(dfs->dfs_pdev_obj) ==
720 			 DFS_FCC_DOMAIN)) {
721 			if (insize < sizeof(uint32_t) || !indata) {
722 				error = -EINVAL;
723 				break;
724 			}
725 			dfs_set_disable_radar_marking(dfs, *(uint8_t *)indata);
726 		}
727 		break;
728 	case DFS_GET_DISABLE_RADAR_MARKING:
729 		if (!outdata || !outsize || *outsize < sizeof(uint8_t)) {
730 			error = -EINVAL;
731 			break;
732 		}
733 		if (dfs->dfs_is_offload_enabled) {
734 			*outsize = sizeof(uint8_t);
735 			*((uint8_t *)outdata) =
736 				dfs_get_disable_radar_marking(dfs);
737 		}
738 		break;
739 	case DFS_GET_NOL:
740 		if (!outdata || !outsize ||
741 				*outsize < sizeof(struct dfsreq_nolinfo)) {
742 			error = -EINVAL;
743 			break;
744 		}
745 		*outsize = sizeof(struct dfsreq_nolinfo);
746 		nol = (struct dfsreq_nolinfo *)outdata;
747 		DFS_GET_NOL_LOCKED(dfs,
748 				(struct dfsreq_nolelem *)nol->dfs_nol,
749 				&nol->dfs_ch_nchans);
750 		DFS_PRINT_NOL_LOCKED(dfs);
751 		break;
752 	case DFS_SET_NOL:
753 		if (insize < sizeof(struct dfsreq_nolinfo) || !indata) {
754 			error = -EINVAL;
755 			break;
756 		}
757 		nol = (struct dfsreq_nolinfo *) indata;
758 		dfs_set_nol(dfs,
759 				(struct dfsreq_nolelem *)nol->dfs_nol,
760 				nol->dfs_ch_nchans);
761 		break;
762 	case DFS_SHOW_NOL:
763 		DFS_PRINT_NOL_LOCKED(dfs);
764 		break;
765 	case DFS_SHOW_NOLHISTORY:
766 		dfs_print_nolhistory(dfs);
767 		break;
768 	case DFS_SHOW_PRECAC_LISTS:
769 		dfs_print_precaclists(dfs);
770 		break;
771 	case DFS_RESET_PRECAC_LISTS:
772 		dfs_reset_precac_lists(dfs);
773 		break;
774 	case DFS_INJECT_SEQUENCE:
775 		error = dfs_inject_synthetic_pulse_sequence(dfs, indata);
776 		if (error)
777 			dfs_debug(dfs, WLAN_DEBUG_DFS_ALWAYS,
778 				  "Not injected Synthetic pulse");
779 		break;
780 
781 	case DFS_ALLOW_HW_PULSES:
782 		if (insize < sizeof(u_int8_t) || !indata) {
783 			error = -EINVAL;
784 			break;
785 		}
786 		dfs_allow_hw_pulses(dfs, !!(*(u_int8_t *)indata));
787 		break;
788 	case DFS_SET_PRI_MULTIPILER:
789 		dfs->dfs_pri_multiplier = *(int *)indata;
790 		dfs_debug(dfs, WLAN_DEBUG_DFS_ALWAYS,
791 			  "Set dfs pri multiplier to %d, dfsdomain %d",
792 			  dfs->dfs_pri_multiplier, dfs->dfsdomain);
793 		break;
794 	default:
795 		error = -EINVAL;
796 	}
797 
798 bad:
799 	return error;
800 }
801 
802 /**
803  * dfs_is_curchan_same_as_given_chan() - Find if dfs_curchan has the same
804  * channel parameters provided.
805  * @dfs_curchan: Pointer to DFS current channel structure.
806  * @dfs_ch_freq: New curchan's primary frequency.
807  * @dfs_ch_flags: New curchan's channel flags.
808  * @dfs_ch_flagext: New curchan's channel flags extension.
809  * @dfs_ch_vhtop_ch_freq_seg1: New curchan's primary centre IEEE.
810  * @dfs_ch_vhtop_ch_freq_seg2: New curchan's secondary centre IEEE.
811  *
812  * Return: True if curchan has the same channel parameters of the given channel,
813  * else false.
814  */
815 static bool
816 dfs_is_curchan_same_as_given_chan(struct dfs_channel *dfs_curchan,
817 				  uint16_t dfs_ch_freq,
818 				  uint64_t dfs_ch_flags,
819 				  uint16_t dfs_ch_flagext,
820 				  uint8_t dfs_ch_vhtop_ch_freq_seg1,
821 				  uint8_t dfs_ch_vhtop_ch_freq_seg2)
822 {
823 	if ((dfs_curchan->dfs_ch_freq == dfs_ch_freq) &&
824 	    (dfs_curchan->dfs_ch_flags == dfs_ch_flags) &&
825 	    (dfs_curchan->dfs_ch_flagext == dfs_ch_flagext) &&
826 	    (dfs_curchan->dfs_ch_vhtop_ch_freq_seg1 ==
827 	     dfs_ch_vhtop_ch_freq_seg1) &&
828 	    (dfs_curchan->dfs_ch_vhtop_ch_freq_seg2 ==
829 	     dfs_ch_vhtop_ch_freq_seg2))
830 		return true;
831 
832 	return false;
833 }
834 
835 #ifdef CONFIG_CHAN_NUM_API
836 void dfs_set_current_channel(struct wlan_dfs *dfs,
837 		uint16_t dfs_ch_freq,
838 		uint64_t dfs_ch_flags,
839 		uint16_t dfs_ch_flagext,
840 		uint8_t dfs_ch_ieee,
841 		uint8_t dfs_ch_vhtop_ch_freq_seg1,
842 		uint8_t dfs_ch_vhtop_ch_freq_seg2)
843 {
844 	if (!dfs) {
845 		dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS,  "dfs is NULL");
846 		return;
847 	}
848 
849 	if (!dfs->dfs_curchan) {
850 		dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS,  "dfs_curchan is NULL");
851 		return;
852 	}
853 
854 	/* Check if the input parameters are the same as that of dfs_curchan */
855 	if (dfs_is_curchan_same_as_given_chan(dfs->dfs_curchan,
856 					      dfs_ch_freq,
857 					      dfs_ch_flags,
858 					      dfs_ch_flagext,
859 					      dfs_ch_vhtop_ch_freq_seg1,
860 					      dfs_ch_vhtop_ch_freq_seg2)) {
861 		dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS,
862 			 "dfs_curchan already updated");
863 		return;
864 	}
865 
866 	/* Update dfs previous channel with the old dfs_curchan, if it exists */
867 	if (dfs->dfs_curchan->dfs_ch_freq)
868 		qdf_mem_copy(dfs->dfs_prevchan,
869 			     dfs->dfs_curchan,
870 			     sizeof(struct dfs_channel));
871 
872 	dfs->dfs_curchan->dfs_ch_freq = dfs_ch_freq;
873 	dfs->dfs_curchan->dfs_ch_flags = dfs_ch_flags;
874 	dfs->dfs_curchan->dfs_ch_flagext = dfs_ch_flagext;
875 	dfs->dfs_curchan->dfs_ch_ieee = dfs_ch_ieee;
876 	dfs->dfs_curchan->dfs_ch_vhtop_ch_freq_seg1 = dfs_ch_vhtop_ch_freq_seg1;
877 	dfs->dfs_curchan->dfs_ch_vhtop_ch_freq_seg2 = dfs_ch_vhtop_ch_freq_seg2;
878 }
879 #endif
880 
881 #ifdef CONFIG_CHAN_FREQ_API
882 void dfs_set_current_channel_for_freq(struct wlan_dfs *dfs,
883 				      uint16_t dfs_chan_freq,
884 				      uint64_t dfs_chan_flags,
885 				      uint16_t dfs_chan_flagext,
886 				      uint8_t dfs_chan_ieee,
887 				      uint8_t dfs_chan_vhtop_freq_seg1,
888 				      uint8_t dfs_chan_vhtop_freq_seg2,
889 				      uint16_t dfs_chan_mhz_freq_seg1,
890 				      uint16_t dfs_chan_mhz_freq_seg2,
891 				      bool *is_channel_updated)
892 {
893 	if (is_channel_updated)
894 		*is_channel_updated = false;
895 
896 	if (!dfs) {
897 		dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS,  "dfs is NULL");
898 		return;
899 	}
900 
901 	/* Check if the input parameters are the same as that of dfs_curchan */
902 	if (dfs_is_curchan_same_as_given_chan(dfs->dfs_curchan,
903 					      dfs_chan_freq,
904 					      dfs_chan_flags,
905 					      dfs_chan_flagext,
906 					      dfs_chan_vhtop_freq_seg1,
907 					      dfs_chan_vhtop_freq_seg2)) {
908 		dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS,
909 			 "dfs_curchan already updated");
910 		return;
911 	}
912 
913 	/* Update dfs previous channel with the old dfs_curchan, if it exists */
914 	if (dfs->dfs_curchan->dfs_ch_freq)
915 		qdf_mem_copy(dfs->dfs_prevchan,
916 			     dfs->dfs_curchan,
917 			     sizeof(struct dfs_channel));
918 
919 	dfs->dfs_curchan->dfs_ch_freq = dfs_chan_freq;
920 	dfs->dfs_curchan->dfs_ch_flags = dfs_chan_flags;
921 	dfs->dfs_curchan->dfs_ch_flagext = dfs_chan_flagext;
922 	dfs->dfs_curchan->dfs_ch_ieee = dfs_chan_ieee;
923 	dfs->dfs_curchan->dfs_ch_vhtop_ch_freq_seg1 = dfs_chan_vhtop_freq_seg1;
924 	dfs->dfs_curchan->dfs_ch_vhtop_ch_freq_seg2 = dfs_chan_vhtop_freq_seg2;
925 	dfs->dfs_curchan->dfs_ch_mhz_freq_seg1 = dfs_chan_mhz_freq_seg1;
926 	dfs->dfs_curchan->dfs_ch_mhz_freq_seg2 = dfs_chan_mhz_freq_seg2;
927 
928 	if (is_channel_updated)
929 		*is_channel_updated = true;
930 }
931 #endif
932 
933 void dfs_update_cur_chan_flags(struct wlan_dfs *dfs,
934 		uint64_t flags,
935 		uint16_t flagext)
936 {
937 	dfs->dfs_curchan->dfs_ch_flags = flags;
938 	dfs->dfs_curchan->dfs_ch_flagext = flagext;
939 }
940 
941 int dfs_reinit_timers(struct wlan_dfs *dfs)
942 {
943 	dfs_cac_timer_attach(dfs);
944 	dfs_zero_cac_timer_init(dfs->dfs_soc_obj);
945 	dfs_nol_timer_init(dfs);
946 	dfs_main_task_testtimer_init(dfs);
947 	return 0;
948 }
949 
950 void dfs_reset_dfs_prevchan(struct wlan_dfs *dfs)
951 {
952 	qdf_mem_zero(dfs->dfs_prevchan, sizeof(struct dfs_channel));
953 }
954 
955 bool dfs_is_hw_mode_switch_in_progress(struct wlan_dfs *dfs)
956 {
957 	return lmac_dfs_is_hw_mode_switch_in_progress(dfs->dfs_pdev_obj);
958 }
959 
960 void dfs_complete_deferred_tasks(struct wlan_dfs *dfs)
961 {
962 	if (dfs->dfs_defer_params.is_radar_detected) {
963 		/* Handle radar event that was deferred and free the temporary
964 		 * storage of the radar event parameters.
965 		 */
966 		dfs_process_radar_ind(dfs, dfs->dfs_defer_params.radar_params);
967 		qdf_mem_free(dfs->dfs_defer_params.radar_params);
968 		dfs->dfs_defer_params.is_radar_detected = false;
969 	} else if (dfs->dfs_defer_params.is_cac_completed) {
970 		/* Handle CAC completion event that was deferred for HW mode
971 		 * switch.
972 		 */
973 		dfs_process_cac_completion(dfs);
974 		dfs->dfs_defer_params.is_cac_completed = false;
975 	}
976 }
977 
978 #ifdef WLAN_DFS_TRUE_160MHZ_SUPPORT
979 bool dfs_is_true_160mhz_supported(struct wlan_dfs *dfs)
980 {
981 	struct wlan_objmgr_psoc *psoc = dfs->dfs_soc_obj->psoc;
982 	struct wlan_lmac_if_target_tx_ops *tgt_tx_ops;
983 	struct wlan_lmac_if_tx_ops *tx_ops;
984 	uint32_t target_type;
985 
986 	tx_ops = wlan_psoc_get_lmac_if_txops(psoc);
987 	if (!tx_ops) {
988 		 dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS, "tx_ops is NULL");
989 		 return false;
990 	}
991 	target_type = lmac_get_target_type(dfs->dfs_pdev_obj);
992 	tgt_tx_ops = &tx_ops->target_tx_ops;
993 	if (tgt_tx_ops->tgt_is_tgt_type_qcn9000 &&
994 	    tgt_tx_ops->tgt_is_tgt_type_qcn9000(target_type))
995 		return true;
996 
997 	if (tgt_tx_ops->tgt_is_tgt_type_qcn9100 &&
998 	    tgt_tx_ops->tgt_is_tgt_type_qcn9100(target_type))
999 		return true;
1000 
1001 	return false;
1002 }
1003 
1004 bool dfs_is_restricted_80p80mhz_supported(struct wlan_dfs *dfs)
1005 {
1006 	return wlan_psoc_nif_fw_ext_cap_get(dfs->dfs_soc_obj->psoc,
1007 					    WLAN_SOC_RESTRICTED_80P80_SUPPORT);
1008 }
1009 #endif
1010 
1011 #ifdef QCA_SUPPORT_AGILE_DFS
1012 uint8_t dfs_get_agile_detector_id(struct wlan_dfs *dfs)
1013 {
1014 	return dfs->dfs_agile_detector_id;
1015 }
1016 #endif
1017 
1018 #ifdef QCA_SUPPORT_DFS_CHAN_POSTNOL
1019 void dfs_set_postnol_freq(struct wlan_dfs *dfs, qdf_freq_t postnol_freq)
1020 {
1021 	dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS,
1022 		 "dfs_chan_postnol_freq configured as %d", postnol_freq);
1023 
1024 	dfs->dfs_chan_postnol_freq = postnol_freq;
1025 }
1026 
1027 void dfs_set_postnol_mode(struct wlan_dfs *dfs, uint8_t postnol_mode)
1028 {
1029 	if (dfs->dfs_chan_postnol_cfreq2) {
1030 		dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS,
1031 			 "postNOL cfreq2 has been set,reset it to change mode");
1032 		return;
1033 	}
1034 
1035 	switch (postnol_mode) {
1036 	case DFS_CHWIDTH_20_VAL:
1037 		dfs->dfs_chan_postnol_mode = CH_WIDTH_20MHZ;
1038 		break;
1039 	case DFS_CHWIDTH_40_VAL:
1040 		dfs->dfs_chan_postnol_mode = CH_WIDTH_40MHZ;
1041 		break;
1042 	case DFS_CHWIDTH_80_VAL:
1043 		dfs->dfs_chan_postnol_mode = CH_WIDTH_80MHZ;
1044 		break;
1045 	case DFS_CHWIDTH_160_VAL:
1046 		dfs->dfs_chan_postnol_mode = CH_WIDTH_160MHZ;
1047 		break;
1048 	default:
1049 		dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS,
1050 			"Invalid postNOL mode configured");
1051 		return;
1052 	}
1053 
1054 	dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS,
1055 		 "DFS postnol mode configured as %d",
1056 		 dfs->dfs_chan_postnol_mode);
1057 }
1058 
1059 void dfs_set_postnol_cfreq2(struct wlan_dfs *dfs, qdf_freq_t postnol_cfreq2)
1060 {
1061 	dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS,
1062 		 "dfs_chan_postnol_cfreq2 configured as %d", postnol_cfreq2);
1063 
1064 	dfs->dfs_chan_postnol_cfreq2 = postnol_cfreq2;
1065 
1066 	if (postnol_cfreq2) {
1067 		dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS,
1068 			 "postNOL cfreq2 is set, changing mode to 80P80");
1069 		dfs->dfs_chan_postnol_mode = CH_WIDTH_80P80MHZ;
1070 	}
1071 }
1072 
1073 void dfs_get_postnol_freq(struct wlan_dfs *dfs, qdf_freq_t *postnol_freq)
1074 {
1075 	*postnol_freq = dfs->dfs_chan_postnol_freq;
1076 }
1077 
1078 void dfs_get_postnol_mode(struct wlan_dfs *dfs, uint8_t *postnol_mode)
1079 {
1080 	*postnol_mode = dfs->dfs_chan_postnol_mode;
1081 }
1082 
1083 void dfs_get_postnol_cfreq2(struct wlan_dfs *dfs, qdf_freq_t *postnol_cfreq2)
1084 {
1085 	*postnol_cfreq2 = dfs->dfs_chan_postnol_cfreq2;
1086 }
1087 #endif
1088