1 /*
2  * Copyright (c) 2014-2020 The Linux Foundation. All rights reserved.
3  * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
4  *
5  * Permission to use, copy, modify, and/or distribute this software for
6  * any purpose with or without fee is hereby granted, provided that the
7  * above copyright notice and this permission notice appear in all
8  * copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
11  * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
12  * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
13  * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
14  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
15  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
16  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17  * PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 /**
21  * DOC: reg_lte.c
22  * This file contains the LTE feature APIs.
23  */
24 
25 #include <qdf_status.h>
26 #include <qdf_types.h>
27 #include <wlan_cmn.h>
28 #include "reg_services_public_struct.h"
29 #include <wlan_objmgr_psoc_obj.h>
30 #include <wlan_objmgr_pdev_obj.h>
31 #include "reg_priv_objs.h"
32 #include "reg_services_common.h"
33 #include "reg_build_chan_list.h"
34 #include "reg_callbacks.h"
35 #include "reg_lte.h"
36 
37 #ifdef LTE_COEX
38 /**
39  * reg_process_ch_avoid_freq() - Update unsafe frequencies in psoc_priv_obj
40  * @psoc: pointer to psoc object
41  * @pdev: pointer to pdev object
42  *
43  * Return: QDF_STATUS
44  */
reg_process_ch_avoid_freq(struct wlan_objmgr_psoc * psoc,struct wlan_objmgr_pdev * pdev)45 static QDF_STATUS reg_process_ch_avoid_freq(struct wlan_objmgr_psoc *psoc,
46 					    struct wlan_objmgr_pdev *pdev)
47 {
48 	enum channel_enum ch_loop;
49 	enum channel_enum start_ch_idx;
50 	enum channel_enum end_ch_idx;
51 	uint8_t start_channel;
52 	uint8_t end_channel;
53 	uint32_t i;
54 	struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj;
55 	struct ch_avoid_freq_type *range;
56 
57 	psoc_priv_obj = reg_get_psoc_obj(psoc);
58 	if (!psoc_priv_obj) {
59 		reg_err("reg psoc private obj is NULL");
60 		return QDF_STATUS_E_FAILURE;
61 	}
62 
63 	for (i = 0; i < psoc_priv_obj->avoid_freq_list.ch_avoid_range_cnt;
64 		i++) {
65 		if (psoc_priv_obj->unsafe_chan_list.chan_cnt >= NUM_CHANNELS) {
66 			reg_warn("LTE Coex unsafe channel list full");
67 			break;
68 		}
69 
70 		start_ch_idx = INVALID_CHANNEL;
71 		end_ch_idx = INVALID_CHANNEL;
72 		range = &psoc_priv_obj->avoid_freq_list.avoid_freq_range[i];
73 
74 		start_channel = reg_freq_to_chan(pdev, range->start_freq);
75 		end_channel = reg_freq_to_chan(pdev, range->end_freq);
76 		reg_debug("start: freq %d, ch %d, end: freq %d, ch %d",
77 			  range->start_freq, start_channel, range->end_freq,
78 			  end_channel);
79 
80 		/* do not process frequency bands that are not mapped to
81 		 * predefined channels
82 		 */
83 		if (start_channel == 0 || end_channel == 0)
84 			continue;
85 
86 		for (ch_loop = 0; ch_loop < NUM_CHANNELS;
87 			ch_loop++) {
88 			if (REG_CH_TO_FREQ(ch_loop) >= range->start_freq) {
89 				start_ch_idx = ch_loop;
90 				break;
91 			}
92 		}
93 		for (ch_loop = 0; ch_loop < NUM_CHANNELS;
94 			ch_loop++) {
95 			if (REG_CH_TO_FREQ(ch_loop) >= range->end_freq) {
96 				end_ch_idx = ch_loop;
97 				if (REG_CH_TO_FREQ(ch_loop) > range->end_freq)
98 					end_ch_idx--;
99 				break;
100 			}
101 		}
102 
103 		if (reg_is_chan_enum_invalid(start_ch_idx) ||
104 		    reg_is_chan_enum_invalid(end_ch_idx))
105 			continue;
106 
107 		for (ch_loop = start_ch_idx; ch_loop <= end_ch_idx;
108 			ch_loop++) {
109 			psoc_priv_obj->unsafe_chan_list.chan_freq_list[
110 				psoc_priv_obj->unsafe_chan_list.chan_cnt++] =
111 				REG_CH_TO_FREQ(ch_loop);
112 			if (psoc_priv_obj->unsafe_chan_list.chan_cnt >=
113 				NUM_CHANNELS) {
114 				reg_warn("LTECoex unsafe ch list full");
115 				break;
116 			}
117 		}
118 	}
119 
120 	if (!psoc_priv_obj->unsafe_chan_list.chan_cnt)
121 		return QDF_STATUS_SUCCESS;
122 
123 	for (ch_loop = 0; ch_loop < psoc_priv_obj->unsafe_chan_list.chan_cnt;
124 		ch_loop++) {
125 		if (ch_loop >= NUM_CHANNELS)
126 			break;
127 		reg_debug("Unsafe freq %d",
128 			  psoc_priv_obj->unsafe_chan_list.chan_freq_list[ch_loop]);
129 	}
130 
131 	return QDF_STATUS_SUCCESS;
132 }
133 
134 /**
135  * reg_update_unsafe_ch() - Updates unsafe channels in current channel list
136  * @psoc: Pointer to psoc structure
137  * @object: Pointer to pdev structure
138  * @arg: List of arguments
139  *
140  * Return: None
141  */
reg_update_unsafe_ch(struct wlan_objmgr_psoc * psoc,void * object,void * arg)142 static void reg_update_unsafe_ch(struct wlan_objmgr_psoc *psoc,
143 				 void *object, void *arg)
144 {
145 	struct wlan_objmgr_pdev *pdev = (struct wlan_objmgr_pdev *)object;
146 	struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj;
147 	struct wlan_regulatory_pdev_priv_obj *pdev_priv_obj;
148 	QDF_STATUS status;
149 
150 	psoc_priv_obj = reg_get_psoc_obj(psoc);
151 	if (!psoc_priv_obj) {
152 		reg_err("reg psoc private obj is NULL");
153 		return;
154 	}
155 
156 	pdev_priv_obj = reg_get_pdev_obj(pdev);
157 
158 	if (!IS_VALID_PDEV_REG_OBJ(pdev_priv_obj)) {
159 		reg_err("reg pdev priv obj is NULL");
160 		return;
161 	}
162 
163 	if (psoc_priv_obj->ch_avoid_ind) {
164 		status = reg_process_ch_avoid_freq(psoc, pdev);
165 		if (QDF_IS_STATUS_ERROR(status))
166 			psoc_priv_obj->ch_avoid_ind = false;
167 	}
168 
169 	reg_compute_pdev_current_chan_list(pdev_priv_obj);
170 	status = reg_send_scheduler_msg_nb(psoc, pdev);
171 
172 	if (QDF_IS_STATUS_ERROR(status))
173 		reg_err("channel change msg schedule failed");
174 }
175 
reg_process_ch_avoid_event(struct wlan_objmgr_psoc * psoc,struct ch_avoid_ind_type * ch_avoid_event)176 QDF_STATUS reg_process_ch_avoid_event(struct wlan_objmgr_psoc *psoc,
177 				      struct ch_avoid_ind_type *ch_avoid_event)
178 {
179 	uint32_t i;
180 	struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj;
181 	QDF_STATUS status;
182 
183 	psoc_priv_obj = reg_get_psoc_obj(psoc);
184 	if (!psoc_priv_obj) {
185 		reg_err("reg psoc private obj is NULL");
186 		return QDF_STATUS_E_FAILURE;
187 	}
188 
189 	if (reg_check_coex_unsafe_nb_user_prefer(psoc)) {
190 		reg_err("skipping LTE Coex unsafe channel change");
191 		return QDF_STATUS_E_FAILURE;
192 	}
193 
194 	if (CH_AVOID_RULE_DO_NOT_RESTART ==
195 	    psoc_priv_obj->restart_beaconing) {
196 		reg_debug("skipping all LTE Coex unsafe channel range");
197 		return QDF_STATUS_SUCCESS;
198 	}
199 	/* Make unsafe channel list */
200 	reg_debug("band count %d", ch_avoid_event->ch_avoid_range_cnt);
201 
202 	/* generate vendor specific event */
203 	qdf_mem_zero(&psoc_priv_obj->avoid_freq_list,
204 		     sizeof(struct ch_avoid_ind_type));
205 	qdf_mem_zero(&psoc_priv_obj->unsafe_chan_list,
206 		     sizeof(struct unsafe_ch_list));
207 
208 	for (i = 0; i < ch_avoid_event->ch_avoid_range_cnt; i++) {
209 		if ((CH_AVOID_RULE_RESTART_24G_ONLY ==
210 				psoc_priv_obj->restart_beaconing) &&
211 			REG_IS_5GHZ_FREQ(ch_avoid_event->
212 				avoid_freq_range[i].start_freq)) {
213 			reg_debug(
214 				  "skipping 5Ghz LTE Coex unsafe channel range");
215 			continue;
216 		}
217 		psoc_priv_obj->avoid_freq_list.avoid_freq_range[i].start_freq =
218 			ch_avoid_event->avoid_freq_range[i].start_freq;
219 		psoc_priv_obj->avoid_freq_list.avoid_freq_range[i].end_freq =
220 			ch_avoid_event->avoid_freq_range[i].end_freq;
221 	}
222 	psoc_priv_obj->avoid_freq_list.ch_avoid_range_cnt =
223 		ch_avoid_event->ch_avoid_range_cnt;
224 
225 	psoc_priv_obj->ch_avoid_ind = true;
226 
227 	status = wlan_objmgr_psoc_try_get_ref(psoc, WLAN_REGULATORY_NB_ID);
228 
229 	if (QDF_IS_STATUS_ERROR(status)) {
230 		reg_err("error taking psoc ref cnt");
231 		return status;
232 	}
233 
234 	status = wlan_objmgr_iterate_obj_list(
235 			psoc, WLAN_PDEV_OP, reg_update_unsafe_ch, NULL, 1,
236 			WLAN_REGULATORY_NB_ID);
237 
238 	wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_NB_ID);
239 
240 	return status;
241 }
242 #endif
243