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 */ 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 */ 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 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