1 /* 2 * Copyright (c) 2014-2019 The Linux Foundation. All rights reserved. 3 * 4 * Permission to use, copy, modify, and/or distribute this software for 5 * any purpose with or without fee is hereby granted, provided that the 6 * above copyright notice and this permission notice appear in all 7 * copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 10 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 11 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 12 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL 13 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR 14 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 15 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 16 * PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 /** 20 * DOC: reg_callbacks.c 21 * This file defines regulatory callback functions 22 */ 23 24 #include <wlan_cmn.h> 25 #include <reg_services_public_struct.h> 26 #include <wlan_objmgr_psoc_obj.h> 27 #include <wlan_objmgr_pdev_obj.h> 28 #include "reg_priv_objs.h" 29 #include "reg_utils.h" 30 #include <scheduler_api.h> 31 #include "reg_callbacks.h" 32 #include "reg_services_common.h" 33 #include "reg_build_chan_list.h" 34 35 /** 36 * reg_call_chan_change_cbks() - Call registered callback functions on channel 37 * change. 38 * @psoc: Pointer to global psoc structure. 39 * @pdev: Pointer to global pdev structure. 40 */ 41 static void reg_call_chan_change_cbks(struct wlan_objmgr_psoc *psoc, 42 struct wlan_objmgr_pdev *pdev) 43 { 44 struct chan_change_cbk_entry *cbk_list; 45 struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj; 46 struct wlan_regulatory_pdev_priv_obj *pdev_priv_obj; 47 struct regulatory_channel *cur_chan_list; 48 uint32_t ctr; 49 struct avoid_freq_ind_data *avoid_freq_ind = NULL; 50 reg_chan_change_callback callback; 51 52 psoc_priv_obj = reg_get_psoc_obj(psoc); 53 if (!IS_VALID_PSOC_REG_OBJ(psoc_priv_obj)) { 54 reg_alert("psoc reg component is NULL"); 55 return; 56 } 57 58 pdev_priv_obj = reg_get_pdev_obj(pdev); 59 if (!IS_VALID_PDEV_REG_OBJ(pdev_priv_obj)) { 60 reg_alert("pdev reg component is NULL"); 61 return; 62 } 63 64 cur_chan_list = qdf_mem_malloc(NUM_CHANNELS * sizeof(*cur_chan_list)); 65 if (!cur_chan_list) { 66 reg_alert("Mem alloc failed for current channel list"); 67 return; 68 } 69 70 qdf_mem_copy(cur_chan_list, 71 pdev_priv_obj->cur_chan_list, 72 NUM_CHANNELS * 73 sizeof(struct regulatory_channel)); 74 75 if (psoc_priv_obj->ch_avoid_ind) { 76 avoid_freq_ind = qdf_mem_malloc(sizeof(*avoid_freq_ind)); 77 if (!avoid_freq_ind) { 78 reg_alert("Mem alloc failed for avoid freq ind"); 79 goto skip_ch_avoid_ind; 80 } 81 qdf_mem_copy(&avoid_freq_ind->freq_list, 82 &psoc_priv_obj->avoid_freq_list, 83 sizeof(struct ch_avoid_ind_type)); 84 qdf_mem_copy(&avoid_freq_ind->chan_list, 85 &psoc_priv_obj->unsafe_chan_list, 86 sizeof(struct unsafe_ch_list)); 87 psoc_priv_obj->ch_avoid_ind = false; 88 } 89 90 skip_ch_avoid_ind: 91 cbk_list = psoc_priv_obj->cbk_list; 92 93 for (ctr = 0; ctr < REG_MAX_CHAN_CHANGE_CBKS; ctr++) { 94 callback = NULL; 95 qdf_spin_lock_bh(&psoc_priv_obj->cbk_list_lock); 96 if (cbk_list[ctr].cbk) 97 callback = cbk_list[ctr].cbk; 98 qdf_spin_unlock_bh(&psoc_priv_obj->cbk_list_lock); 99 if (callback) 100 callback(psoc, pdev, cur_chan_list, avoid_freq_ind, 101 cbk_list[ctr].arg); 102 } 103 qdf_mem_free(cur_chan_list); 104 if (avoid_freq_ind) 105 qdf_mem_free(avoid_freq_ind); 106 } 107 108 /** 109 * reg_alloc_and_fill_payload() - Alloc and fill payload structure. 110 * @psoc: Pointer to global psoc structure. 111 * @pdev: Pointer to global pdev structure. 112 */ 113 static void reg_alloc_and_fill_payload(struct wlan_objmgr_psoc *psoc, 114 struct wlan_objmgr_pdev *pdev, 115 struct reg_sched_payload **payload) 116 { 117 *payload = qdf_mem_malloc(sizeof(**payload)); 118 if (*payload) { 119 (*payload)->psoc = psoc; 120 (*payload)->pdev = pdev; 121 } 122 } 123 124 /** 125 * reg_chan_change_flush_cbk_sb() - Flush south bound channel change callbacks. 126 * @msg: Pointer to scheduler msg structure. 127 */ 128 static QDF_STATUS reg_chan_change_flush_cbk_sb(struct scheduler_msg *msg) 129 { 130 struct reg_sched_payload *load = msg->bodyptr; 131 struct wlan_objmgr_psoc *psoc = load->psoc; 132 struct wlan_objmgr_pdev *pdev = load->pdev; 133 134 wlan_objmgr_pdev_release_ref(pdev, WLAN_REGULATORY_SB_ID); 135 wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_SB_ID); 136 qdf_mem_free(load); 137 138 return QDF_STATUS_SUCCESS; 139 } 140 141 /** 142 * reg_sched_chan_change_cbks_sb() - Schedule south bound channel change 143 * callbacks. 144 * @msg: Pointer to scheduler msg structure. 145 */ 146 static QDF_STATUS reg_sched_chan_change_cbks_sb(struct scheduler_msg *msg) 147 { 148 struct reg_sched_payload *load = msg->bodyptr; 149 struct wlan_objmgr_psoc *psoc = load->psoc; 150 struct wlan_objmgr_pdev *pdev = load->pdev; 151 152 reg_call_chan_change_cbks(psoc, pdev); 153 154 wlan_objmgr_pdev_release_ref(pdev, WLAN_REGULATORY_SB_ID); 155 wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_SB_ID); 156 qdf_mem_free(load); 157 158 return QDF_STATUS_SUCCESS; 159 } 160 161 /** 162 * reg_chan_change_flush_cbk_nb() - Flush north bound channel change callbacks. 163 * @msg: Pointer to scheduler msg structure. 164 */ 165 static QDF_STATUS reg_chan_change_flush_cbk_nb(struct scheduler_msg *msg) 166 { 167 struct reg_sched_payload *load = msg->bodyptr; 168 struct wlan_objmgr_psoc *psoc = load->psoc; 169 struct wlan_objmgr_pdev *pdev = load->pdev; 170 171 wlan_objmgr_pdev_release_ref(pdev, WLAN_REGULATORY_NB_ID); 172 wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_NB_ID); 173 qdf_mem_free(load); 174 175 return QDF_STATUS_SUCCESS; 176 } 177 178 /** 179 * reg_sched_chan_change_cbks_nb() - Schedule north bound channel change 180 * callbacks. 181 * @msg: Pointer to scheduler msg structure. 182 */ 183 static QDF_STATUS reg_sched_chan_change_cbks_nb(struct scheduler_msg *msg) 184 { 185 struct reg_sched_payload *load = msg->bodyptr; 186 struct wlan_objmgr_psoc *psoc = load->psoc; 187 struct wlan_objmgr_pdev *pdev = load->pdev; 188 189 reg_call_chan_change_cbks(psoc, pdev); 190 191 wlan_objmgr_pdev_release_ref(pdev, WLAN_REGULATORY_NB_ID); 192 wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_NB_ID); 193 qdf_mem_free(load); 194 195 return QDF_STATUS_SUCCESS; 196 } 197 198 QDF_STATUS reg_send_scheduler_msg_sb(struct wlan_objmgr_psoc *psoc, 199 struct wlan_objmgr_pdev *pdev) 200 { 201 struct scheduler_msg msg = {0}; 202 struct reg_sched_payload *payload; 203 struct wlan_regulatory_pdev_priv_obj *pdev_priv_obj; 204 QDF_STATUS status; 205 206 pdev_priv_obj = reg_get_pdev_obj(pdev); 207 if (!IS_VALID_PDEV_REG_OBJ(pdev_priv_obj)) { 208 reg_alert("pdev reg component is NULL"); 209 return QDF_STATUS_E_FAILURE; 210 } 211 212 if (!pdev_priv_obj->chan_list_recvd) { 213 reg_err("channel list is empty"); 214 return QDF_STATUS_E_FAILURE; 215 } 216 217 status = wlan_objmgr_psoc_try_get_ref(psoc, WLAN_REGULATORY_SB_ID); 218 if (QDF_IS_STATUS_ERROR(status)) { 219 reg_err("error taking psoc ref cnt"); 220 return status; 221 } 222 223 status = wlan_objmgr_pdev_try_get_ref(pdev, WLAN_REGULATORY_SB_ID); 224 if (QDF_IS_STATUS_ERROR(status)) { 225 wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_SB_ID); 226 reg_err("error taking pdev ref cnt"); 227 return status; 228 } 229 230 reg_alloc_and_fill_payload(psoc, pdev, &payload); 231 if (!payload) { 232 reg_err("payload memory alloc failed"); 233 wlan_objmgr_pdev_release_ref(pdev, WLAN_REGULATORY_SB_ID); 234 wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_SB_ID); 235 return QDF_STATUS_E_NOMEM; 236 } 237 238 msg.bodyptr = payload; 239 msg.callback = reg_sched_chan_change_cbks_sb; 240 msg.flush_callback = reg_chan_change_flush_cbk_sb; 241 242 status = scheduler_post_message(QDF_MODULE_ID_REGULATORY, 243 QDF_MODULE_ID_REGULATORY, 244 QDF_MODULE_ID_TARGET_IF, &msg); 245 if (QDF_IS_STATUS_ERROR(status)) { 246 wlan_objmgr_pdev_release_ref(pdev, WLAN_REGULATORY_SB_ID); 247 wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_SB_ID); 248 reg_err("scheduler msg posting failed"); 249 qdf_mem_free(payload); 250 } 251 252 return status; 253 } 254 255 QDF_STATUS reg_send_scheduler_msg_nb(struct wlan_objmgr_psoc *psoc, 256 struct wlan_objmgr_pdev *pdev) 257 { 258 struct scheduler_msg msg = {0}; 259 struct reg_sched_payload *payload; 260 struct wlan_regulatory_pdev_priv_obj *pdev_priv_obj; 261 QDF_STATUS status; 262 263 pdev_priv_obj = reg_get_pdev_obj(pdev); 264 if (!IS_VALID_PDEV_REG_OBJ(pdev_priv_obj)) { 265 reg_alert("pdev reg component is NULL"); 266 return QDF_STATUS_E_FAILURE; 267 } 268 269 if (!pdev_priv_obj->chan_list_recvd) { 270 reg_err("channel list is empty"); 271 return QDF_STATUS_E_FAILURE; 272 } 273 274 status = wlan_objmgr_psoc_try_get_ref(psoc, WLAN_REGULATORY_NB_ID); 275 if (QDF_IS_STATUS_ERROR(status)) { 276 reg_err("error taking psoc ref cnt"); 277 return status; 278 } 279 280 status = wlan_objmgr_pdev_try_get_ref(pdev, WLAN_REGULATORY_NB_ID); 281 if (QDF_IS_STATUS_ERROR(status)) { 282 wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_NB_ID); 283 reg_err("error taking pdev ref cnt"); 284 return status; 285 } 286 287 reg_alloc_and_fill_payload(psoc, pdev, &payload); 288 if (!payload) { 289 reg_err("payload memory alloc failed"); 290 wlan_objmgr_pdev_release_ref(pdev, WLAN_REGULATORY_NB_ID); 291 wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_NB_ID); 292 return QDF_STATUS_E_NOMEM; 293 } 294 msg.bodyptr = payload; 295 msg.callback = reg_sched_chan_change_cbks_nb; 296 msg.flush_callback = reg_chan_change_flush_cbk_nb; 297 298 status = scheduler_post_message(QDF_MODULE_ID_REGULATORY, 299 QDF_MODULE_ID_REGULATORY, 300 QDF_MODULE_ID_OS_IF, &msg); 301 if (QDF_IS_STATUS_ERROR(status)) { 302 wlan_objmgr_pdev_release_ref(pdev, WLAN_REGULATORY_NB_ID); 303 wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_NB_ID); 304 reg_err("scheduler msg posting failed"); 305 qdf_mem_free(payload); 306 } 307 308 return status; 309 } 310 311 QDF_STATUS reg_notify_sap_event(struct wlan_objmgr_pdev *pdev, 312 bool sap_state) 313 { 314 struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj; 315 struct wlan_regulatory_pdev_priv_obj *pdev_priv_obj; 316 struct wlan_objmgr_psoc *psoc; 317 QDF_STATUS status; 318 319 pdev_priv_obj = reg_get_pdev_obj(pdev); 320 321 if (!IS_VALID_PDEV_REG_OBJ(pdev_priv_obj)) { 322 reg_err("pdev reg component is NULL"); 323 return QDF_STATUS_E_INVAL; 324 } 325 326 psoc = wlan_pdev_get_psoc(pdev); 327 if (!psoc) { 328 reg_err("psoc is NULL"); 329 return QDF_STATUS_E_INVAL; 330 } 331 332 psoc_priv_obj = reg_get_psoc_obj(psoc); 333 if (!IS_VALID_PSOC_REG_OBJ(psoc_priv_obj)) { 334 reg_err("psoc reg component is NULL"); 335 return QDF_STATUS_E_INVAL; 336 } 337 338 reg_info("sap_state: %d", sap_state); 339 340 if (pdev_priv_obj->sap_state == sap_state) 341 return QDF_STATUS_SUCCESS; 342 343 pdev_priv_obj->sap_state = sap_state; 344 set_disable_channel_state(pdev_priv_obj); 345 346 reg_compute_pdev_current_chan_list(pdev_priv_obj); 347 status = reg_send_scheduler_msg_sb(psoc, pdev); 348 349 return status; 350 } 351 352 void reg_register_chan_change_callback(struct wlan_objmgr_psoc *psoc, 353 reg_chan_change_callback cbk, void *arg) 354 { 355 struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj; 356 uint32_t count; 357 358 psoc_priv_obj = reg_get_psoc_obj(psoc); 359 if (!psoc_priv_obj) { 360 reg_err("reg psoc private obj is NULL"); 361 return; 362 } 363 364 qdf_spin_lock_bh(&psoc_priv_obj->cbk_list_lock); 365 for (count = 0; count < REG_MAX_CHAN_CHANGE_CBKS; count++) 366 if (!psoc_priv_obj->cbk_list[count].cbk) { 367 psoc_priv_obj->cbk_list[count].cbk = cbk; 368 psoc_priv_obj->cbk_list[count].arg = arg; 369 psoc_priv_obj->num_chan_change_cbks++; 370 break; 371 } 372 qdf_spin_unlock_bh(&psoc_priv_obj->cbk_list_lock); 373 374 if (count == REG_MAX_CHAN_CHANGE_CBKS) 375 reg_err("callback list is full, could not add the cbk"); 376 } 377 378 void reg_unregister_chan_change_callback(struct wlan_objmgr_psoc *psoc, 379 reg_chan_change_callback cbk) 380 { 381 struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj; 382 uint32_t count; 383 384 psoc_priv_obj = reg_get_psoc_obj(psoc); 385 if (!psoc_priv_obj) { 386 reg_err("reg psoc private obj is NULL"); 387 return; 388 } 389 390 qdf_spin_lock_bh(&psoc_priv_obj->cbk_list_lock); 391 for (count = 0; count < REG_MAX_CHAN_CHANGE_CBKS; count++) 392 if (psoc_priv_obj->cbk_list[count].cbk == cbk) { 393 psoc_priv_obj->cbk_list[count].cbk = NULL; 394 psoc_priv_obj->num_chan_change_cbks--; 395 break; 396 } 397 qdf_spin_unlock_bh(&psoc_priv_obj->cbk_list_lock); 398 399 if (count == REG_MAX_CHAN_CHANGE_CBKS) 400 reg_err("callback not found in the list"); 401 } 402 403