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.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 (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.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 reg_debug("number of unsafe channels is %d ", 121 psoc_priv_obj->unsafe_chan_list.chan_cnt); 122 123 if (!psoc_priv_obj->unsafe_chan_list.chan_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.chan_cnt; 129 ch_loop++) { 130 if (ch_loop >= NUM_CHANNELS) 131 break; 132 reg_debug("channel freq %d is not safe", 133 psoc_priv_obj->unsafe_chan_list.chan_freq_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 if (CH_AVOID_RULE_DO_NOT_RESTART == 194 psoc_priv_obj->restart_beaconing) { 195 reg_debug("skipping all LTE Coex unsafe channel range"); 196 return QDF_STATUS_SUCCESS; 197 } 198 /* Make unsafe channel list */ 199 reg_debug("band count %d", ch_avoid_event->ch_avoid_range_cnt); 200 201 /* generate vendor specific event */ 202 qdf_mem_zero(&psoc_priv_obj->avoid_freq_list, 203 sizeof(struct ch_avoid_ind_type)); 204 qdf_mem_zero(&psoc_priv_obj->unsafe_chan_list, 205 sizeof(struct unsafe_ch_list)); 206 207 for (i = 0; i < ch_avoid_event->ch_avoid_range_cnt; i++) { 208 if ((CH_AVOID_RULE_RESTART_24G_ONLY == 209 psoc_priv_obj->restart_beaconing) && 210 REG_IS_5GHZ_FREQ(ch_avoid_event-> 211 avoid_freq_range[i].start_freq)) { 212 reg_debug( 213 "skipping 5Ghz LTE Coex unsafe channel range"); 214 continue; 215 } 216 psoc_priv_obj->avoid_freq_list.avoid_freq_range[i].start_freq = 217 ch_avoid_event->avoid_freq_range[i].start_freq; 218 psoc_priv_obj->avoid_freq_list.avoid_freq_range[i].end_freq = 219 ch_avoid_event->avoid_freq_range[i].end_freq; 220 } 221 psoc_priv_obj->avoid_freq_list.ch_avoid_range_cnt = 222 ch_avoid_event->ch_avoid_range_cnt; 223 224 psoc_priv_obj->ch_avoid_ind = true; 225 226 status = wlan_objmgr_psoc_try_get_ref(psoc, WLAN_REGULATORY_NB_ID); 227 228 if (QDF_IS_STATUS_ERROR(status)) { 229 reg_err("error taking psoc ref cnt"); 230 return status; 231 } 232 233 status = wlan_objmgr_iterate_obj_list( 234 psoc, WLAN_PDEV_OP, reg_update_unsafe_ch, NULL, 1, 235 WLAN_REGULATORY_NB_ID); 236 237 wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_NB_ID); 238 239 return status; 240 } 241 #endif 242