xref: /wlan-dirver/qca-wifi-host-cmn/umac/regulatory/core/src/reg_lte.c (revision dd4dc88b837a295134aa9869114a2efee0f4894b)
1 /*
2  * Copyright (c) 2014-2019 The Linux Foundation. All rights reserved.
3  *
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_services_common.h"
32 #include "reg_priv_objs.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  */
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 	uint16_t start_channel;
52 	uint16_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.ch_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 (start_ch_idx == INVALID_CHANNEL ||
104 		    end_ch_idx == INVALID_CHANNEL)
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.ch_list[
110 				psoc_priv_obj->unsafe_chan_list.ch_cnt++] =
111 				REG_CH_NUM(ch_loop);
112 			if (psoc_priv_obj->unsafe_chan_list.ch_cnt >=
113 				NUM_CHANNELS) {
114 				reg_warn("LTECoex unsafe ch list full");
115 				break;
116 			}
117 		}
118 	}
119 
120 	reg_debug("number of unsafe channels is %d ",
121 		  psoc_priv_obj->unsafe_chan_list.ch_cnt);
122 
123 	if (!psoc_priv_obj->unsafe_chan_list.ch_cnt) {
124 		reg_debug("No valid ch are present in avoid freq event");
125 		return QDF_STATUS_SUCCESS;
126 	}
127 
128 	for (ch_loop = 0; ch_loop < psoc_priv_obj->unsafe_chan_list.ch_cnt;
129 		ch_loop++) {
130 		if (ch_loop >= NUM_CHANNELS)
131 			break;
132 		reg_debug("channel %d is not safe",
133 			  psoc_priv_obj->unsafe_chan_list.ch_list[ch_loop]);
134 	}
135 
136 	return QDF_STATUS_SUCCESS;
137 }
138 
139 /**
140  * reg_update_unsafe_ch() - Updates unsafe channels in current channel list
141  * @psoc: Pointer to psoc structure
142  * @object: Pointer to pdev structure
143  * @arg: List of arguments
144  *
145  * Return: None
146  */
147 static void reg_update_unsafe_ch(struct wlan_objmgr_psoc *psoc,
148 				 void *object, void *arg)
149 {
150 	struct wlan_objmgr_pdev *pdev = (struct wlan_objmgr_pdev *)object;
151 	struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj;
152 	struct wlan_regulatory_pdev_priv_obj *pdev_priv_obj;
153 	QDF_STATUS status;
154 
155 	psoc_priv_obj = reg_get_psoc_obj(psoc);
156 	if (!psoc_priv_obj) {
157 		reg_err("reg psoc private obj is NULL");
158 		return;
159 	}
160 
161 	pdev_priv_obj = reg_get_pdev_obj(pdev);
162 
163 	if (!IS_VALID_PDEV_REG_OBJ(pdev_priv_obj)) {
164 		reg_err("reg pdev priv obj is NULL");
165 		return;
166 	}
167 
168 	if (psoc_priv_obj->ch_avoid_ind) {
169 		status = reg_process_ch_avoid_freq(psoc, pdev);
170 		if (QDF_IS_STATUS_ERROR(status))
171 			psoc_priv_obj->ch_avoid_ind = false;
172 	}
173 
174 	reg_compute_pdev_current_chan_list(pdev_priv_obj);
175 	status = reg_send_scheduler_msg_nb(psoc, pdev);
176 
177 	if (QDF_IS_STATUS_ERROR(status))
178 		reg_err("channel change msg schedule failed");
179 }
180 
181 QDF_STATUS reg_process_ch_avoid_event(struct wlan_objmgr_psoc *psoc,
182 				      struct ch_avoid_ind_type *ch_avoid_event)
183 {
184 	uint32_t i;
185 	struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj;
186 	QDF_STATUS status;
187 
188 	psoc_priv_obj = reg_get_psoc_obj(psoc);
189 	if (!psoc_priv_obj) {
190 		reg_err("reg psoc private obj is NULL");
191 		return QDF_STATUS_E_FAILURE;
192 	}
193 	/* Make unsafe channel list */
194 	reg_debug("band count %d", ch_avoid_event->ch_avoid_range_cnt);
195 
196 	/* generate vendor specific event */
197 	qdf_mem_zero(&psoc_priv_obj->avoid_freq_list,
198 		     sizeof(struct ch_avoid_ind_type));
199 	qdf_mem_zero(&psoc_priv_obj->unsafe_chan_list,
200 		     sizeof(struct unsafe_ch_list));
201 
202 	for (i = 0; i < ch_avoid_event->ch_avoid_range_cnt; i++) {
203 		if ((CH_AVOID_RULE_RESTART_24G_ONLY ==
204 				psoc_priv_obj->restart_beaconing) &&
205 			REG_IS_5GHZ_FREQ(ch_avoid_event->
206 				avoid_freq_range[i].start_freq)) {
207 			reg_debug(
208 				  "skipping 5Ghz LTE Coex unsafe channel range");
209 			continue;
210 		}
211 		psoc_priv_obj->avoid_freq_list.avoid_freq_range[i].start_freq =
212 			ch_avoid_event->avoid_freq_range[i].start_freq;
213 		psoc_priv_obj->avoid_freq_list.avoid_freq_range[i].end_freq =
214 			ch_avoid_event->avoid_freq_range[i].end_freq;
215 	}
216 	psoc_priv_obj->avoid_freq_list.ch_avoid_range_cnt =
217 		ch_avoid_event->ch_avoid_range_cnt;
218 
219 	psoc_priv_obj->ch_avoid_ind = true;
220 
221 	status = wlan_objmgr_psoc_try_get_ref(psoc, WLAN_REGULATORY_NB_ID);
222 
223 	if (QDF_IS_STATUS_ERROR(status)) {
224 		reg_err("error taking psoc ref cnt");
225 		return status;
226 	}
227 
228 	status = wlan_objmgr_iterate_obj_list(
229 			psoc, WLAN_PDEV_OP, reg_update_unsafe_ch, NULL, 1,
230 			WLAN_REGULATORY_NB_ID);
231 
232 	wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_NB_ID);
233 
234 	return status;
235 }
236 #endif
237