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