1 /* 2 * Copyright (c) 2014-2021 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_callbacks.c 22 * This file defines regulatory callback functions 23 */ 24 25 #include <wlan_cmn.h> 26 #include <reg_services_public_struct.h> 27 #include <wlan_objmgr_psoc_obj.h> 28 #include <wlan_objmgr_pdev_obj.h> 29 #include "reg_priv_objs.h" 30 #include "reg_utils.h" 31 #include <scheduler_api.h> 32 #include "reg_callbacks.h" 33 #include "reg_services_common.h" 34 #include "reg_build_chan_list.h" 35 36 /** 37 * reg_call_chan_change_cbks() - Call registered callback functions on channel 38 * change. 39 * @psoc: Pointer to global psoc structure. 40 * @pdev: Pointer to global pdev structure. 41 * @ch_avoid_ind: if chan avoid indicated 42 * @avoid_info: chan avoid info if @ch_avoid_ind is true 43 */ 44 static void reg_call_chan_change_cbks(struct wlan_objmgr_psoc *psoc, 45 struct wlan_objmgr_pdev *pdev, 46 bool ch_avoid_ind, 47 struct avoid_freq_ind_data *avoid_info) 48 { 49 struct chan_change_cbk_entry *cbk_list; 50 struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj; 51 struct wlan_regulatory_pdev_priv_obj *pdev_priv_obj; 52 struct regulatory_channel *cur_chan_list; 53 uint32_t ctr; 54 struct avoid_freq_ind_data *avoid_freq_ind = NULL; 55 reg_chan_change_callback callback; 56 QDF_STATUS status; 57 58 psoc_priv_obj = reg_get_psoc_obj(psoc); 59 if (!IS_VALID_PSOC_REG_OBJ(psoc_priv_obj)) { 60 reg_alert("psoc reg component is NULL"); 61 return; 62 } 63 64 pdev_priv_obj = reg_get_pdev_obj(pdev); 65 if (!IS_VALID_PDEV_REG_OBJ(pdev_priv_obj)) { 66 reg_alert("pdev reg component is NULL"); 67 return; 68 } 69 70 cur_chan_list = qdf_mem_malloc(NUM_CHANNELS * sizeof(*cur_chan_list)); 71 if (!cur_chan_list) 72 return; 73 74 status = reg_get_pwrmode_chan_list(pdev, cur_chan_list, 75 REG_CURRENT_PWR_MODE); 76 if (!QDF_IS_STATUS_SUCCESS(status)) { 77 qdf_mem_free(cur_chan_list); 78 return; 79 } 80 if (ch_avoid_ind) 81 avoid_freq_ind = avoid_info; 82 83 cbk_list = psoc_priv_obj->cbk_list; 84 85 for (ctr = 0; ctr < REG_MAX_CHAN_CHANGE_CBKS; ctr++) { 86 callback = NULL; 87 qdf_spin_lock_bh(&psoc_priv_obj->cbk_list_lock); 88 if (cbk_list[ctr].cbk) 89 callback = cbk_list[ctr].cbk; 90 qdf_spin_unlock_bh(&psoc_priv_obj->cbk_list_lock); 91 if (callback) 92 callback(psoc, pdev, cur_chan_list, avoid_freq_ind, 93 cbk_list[ctr].arg); 94 } 95 qdf_mem_free(cur_chan_list); 96 } 97 98 #ifdef FEATURE_WLAN_CH_AVOID_EXT 99 static inline void 100 reg_fill_freq_ext_payload(struct reg_sched_payload **payload, 101 struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj) 102 { 103 (*payload)->ch_avoid_ind = 104 !!psoc_priv_obj->ch_avoid_ext_ind; 105 qdf_mem_copy(&(*payload)->avoid_info.freq_list, 106 &psoc_priv_obj->avoid_freq_ext_list, 107 sizeof(psoc_priv_obj->avoid_freq_ext_list)); 108 109 psoc_priv_obj->ch_avoid_ext_ind = false; 110 } 111 #else 112 static inline void 113 reg_fill_freq_ext_payload(struct reg_sched_payload **payload, 114 struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj) 115 { 116 } 117 #endif 118 119 /** 120 * reg_alloc_and_fill_payload() - Alloc and fill payload structure. 121 * @psoc: Pointer to global psoc structure. 122 * @pdev: Pointer to global pdev structure. 123 */ 124 static void reg_alloc_and_fill_payload(struct wlan_objmgr_psoc *psoc, 125 struct wlan_objmgr_pdev *pdev, 126 struct reg_sched_payload **payload) 127 { 128 struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj; 129 130 psoc_priv_obj = reg_get_psoc_obj(psoc); 131 if (!psoc_priv_obj) { 132 reg_err("reg psoc private obj is NULL"); 133 *payload = NULL; 134 return; 135 } 136 137 *payload = qdf_mem_malloc(sizeof(**payload)); 138 if (*payload) { 139 (*payload)->psoc = psoc; 140 (*payload)->pdev = pdev; 141 if (reg_check_coex_unsafe_nb_user_prefer(psoc)) { 142 reg_fill_freq_ext_payload(payload, psoc_priv_obj); 143 } else { 144 (*payload)->ch_avoid_ind = 145 !!psoc_priv_obj->ch_avoid_ind; 146 qdf_mem_copy(&(*payload)->avoid_info.freq_list, 147 &psoc_priv_obj->avoid_freq_list, 148 sizeof(psoc_priv_obj->avoid_freq_list)); 149 150 psoc_priv_obj->ch_avoid_ind = false; 151 } 152 qdf_mem_copy(&(*payload)->avoid_info.chan_list, 153 &psoc_priv_obj->unsafe_chan_list, 154 sizeof(psoc_priv_obj->unsafe_chan_list)); 155 } 156 } 157 158 /** 159 * reg_chan_change_flush_cbk_sb() - Flush south bound channel change callbacks. 160 * @msg: Pointer to scheduler msg structure. 161 */ 162 static QDF_STATUS reg_chan_change_flush_cbk_sb(struct scheduler_msg *msg) 163 { 164 struct reg_sched_payload *load = msg->bodyptr; 165 struct wlan_objmgr_psoc *psoc = load->psoc; 166 struct wlan_objmgr_pdev *pdev = load->pdev; 167 168 wlan_objmgr_pdev_release_ref(pdev, WLAN_REGULATORY_SB_ID); 169 wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_SB_ID); 170 qdf_mem_free(load); 171 172 return QDF_STATUS_SUCCESS; 173 } 174 175 /** 176 * reg_sched_chan_change_cbks_sb() - Schedule south bound channel change 177 * callbacks. 178 * @msg: Pointer to scheduler msg structure. 179 */ 180 static QDF_STATUS reg_sched_chan_change_cbks_sb(struct scheduler_msg *msg) 181 { 182 struct reg_sched_payload *load = msg->bodyptr; 183 struct wlan_objmgr_psoc *psoc = load->psoc; 184 struct wlan_objmgr_pdev *pdev = load->pdev; 185 186 reg_call_chan_change_cbks(psoc, pdev, load->ch_avoid_ind, 187 &load->avoid_info); 188 189 wlan_objmgr_pdev_release_ref(pdev, WLAN_REGULATORY_SB_ID); 190 wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_SB_ID); 191 qdf_mem_free(load); 192 193 return QDF_STATUS_SUCCESS; 194 } 195 196 /** 197 * reg_chan_change_flush_cbk_nb() - Flush north bound channel change callbacks. 198 * @msg: Pointer to scheduler msg structure. 199 */ 200 static QDF_STATUS reg_chan_change_flush_cbk_nb(struct scheduler_msg *msg) 201 { 202 struct reg_sched_payload *load = msg->bodyptr; 203 struct wlan_objmgr_psoc *psoc = load->psoc; 204 struct wlan_objmgr_pdev *pdev = load->pdev; 205 206 wlan_objmgr_pdev_release_ref(pdev, WLAN_REGULATORY_NB_ID); 207 wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_NB_ID); 208 qdf_mem_free(load); 209 210 return QDF_STATUS_SUCCESS; 211 } 212 213 /** 214 * reg_sched_chan_change_cbks_nb() - Schedule north bound channel change 215 * callbacks. 216 * @msg: Pointer to scheduler msg structure. 217 */ 218 static QDF_STATUS reg_sched_chan_change_cbks_nb(struct scheduler_msg *msg) 219 { 220 struct reg_sched_payload *load = msg->bodyptr; 221 struct wlan_objmgr_psoc *psoc = load->psoc; 222 struct wlan_objmgr_pdev *pdev = load->pdev; 223 224 reg_call_chan_change_cbks(psoc, pdev, load->ch_avoid_ind, 225 &load->avoid_info); 226 227 wlan_objmgr_pdev_release_ref(pdev, WLAN_REGULATORY_NB_ID); 228 wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_NB_ID); 229 qdf_mem_free(load); 230 231 return QDF_STATUS_SUCCESS; 232 } 233 234 QDF_STATUS reg_send_scheduler_msg_sb(struct wlan_objmgr_psoc *psoc, 235 struct wlan_objmgr_pdev *pdev) 236 { 237 struct scheduler_msg msg = {0}; 238 struct reg_sched_payload *payload; 239 struct wlan_regulatory_pdev_priv_obj *pdev_priv_obj; 240 QDF_STATUS status; 241 242 pdev_priv_obj = reg_get_pdev_obj(pdev); 243 if (!IS_VALID_PDEV_REG_OBJ(pdev_priv_obj)) { 244 reg_alert("pdev reg component is NULL"); 245 return QDF_STATUS_E_FAILURE; 246 } 247 248 if (!pdev_priv_obj->pdev_opened) { 249 reg_err("hlos not initialized"); 250 return QDF_STATUS_E_FAILURE; 251 } 252 253 if (!pdev_priv_obj->chan_list_recvd) { 254 reg_err("Empty channel list"); 255 return QDF_STATUS_E_FAILURE; 256 } 257 258 status = wlan_objmgr_psoc_try_get_ref(psoc, WLAN_REGULATORY_SB_ID); 259 if (QDF_IS_STATUS_ERROR(status)) { 260 reg_err("error taking psoc ref cnt"); 261 return status; 262 } 263 264 status = wlan_objmgr_pdev_try_get_ref(pdev, WLAN_REGULATORY_SB_ID); 265 if (QDF_IS_STATUS_ERROR(status)) { 266 wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_SB_ID); 267 reg_err("error taking pdev ref cnt"); 268 return status; 269 } 270 271 reg_alloc_and_fill_payload(psoc, pdev, &payload); 272 if (!payload) { 273 reg_err("malloc failed"); 274 wlan_objmgr_pdev_release_ref(pdev, WLAN_REGULATORY_SB_ID); 275 wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_SB_ID); 276 return QDF_STATUS_E_NOMEM; 277 } 278 279 msg.bodyptr = payload; 280 msg.callback = reg_sched_chan_change_cbks_sb; 281 msg.flush_callback = reg_chan_change_flush_cbk_sb; 282 283 status = scheduler_post_message(QDF_MODULE_ID_REGULATORY, 284 QDF_MODULE_ID_REGULATORY, 285 QDF_MODULE_ID_TARGET_IF, &msg); 286 if (QDF_IS_STATUS_ERROR(status)) { 287 wlan_objmgr_pdev_release_ref(pdev, WLAN_REGULATORY_SB_ID); 288 wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_SB_ID); 289 qdf_mem_free(payload); 290 } 291 292 return status; 293 } 294 295 QDF_STATUS reg_send_scheduler_msg_nb(struct wlan_objmgr_psoc *psoc, 296 struct wlan_objmgr_pdev *pdev) 297 { 298 struct scheduler_msg msg = {0}; 299 struct reg_sched_payload *payload; 300 struct wlan_regulatory_pdev_priv_obj *pdev_priv_obj; 301 QDF_STATUS status; 302 303 pdev_priv_obj = reg_get_pdev_obj(pdev); 304 if (!IS_VALID_PDEV_REG_OBJ(pdev_priv_obj)) { 305 reg_alert("pdev reg component is NULL"); 306 return QDF_STATUS_E_FAILURE; 307 } 308 309 if (!pdev_priv_obj->pdev_opened) { 310 reg_err("hlos not initialized"); 311 return QDF_STATUS_E_FAILURE; 312 } 313 314 if (!pdev_priv_obj->chan_list_recvd) { 315 reg_err("Empty channel list"); 316 return QDF_STATUS_E_FAILURE; 317 } 318 319 status = wlan_objmgr_psoc_try_get_ref(psoc, WLAN_REGULATORY_NB_ID); 320 if (QDF_IS_STATUS_ERROR(status)) { 321 reg_err("error taking psoc ref cnt"); 322 return status; 323 } 324 325 status = wlan_objmgr_pdev_try_get_ref(pdev, WLAN_REGULATORY_NB_ID); 326 if (QDF_IS_STATUS_ERROR(status)) { 327 wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_NB_ID); 328 reg_err("error taking pdev ref cnt"); 329 return status; 330 } 331 332 reg_alloc_and_fill_payload(psoc, pdev, &payload); 333 if (!payload) { 334 reg_err("malloc failed"); 335 wlan_objmgr_pdev_release_ref(pdev, WLAN_REGULATORY_NB_ID); 336 wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_NB_ID); 337 return QDF_STATUS_E_NOMEM; 338 } 339 msg.bodyptr = payload; 340 msg.callback = reg_sched_chan_change_cbks_nb; 341 msg.flush_callback = reg_chan_change_flush_cbk_nb; 342 343 status = scheduler_post_message(QDF_MODULE_ID_REGULATORY, 344 QDF_MODULE_ID_REGULATORY, 345 QDF_MODULE_ID_OS_IF, &msg); 346 if (QDF_IS_STATUS_ERROR(status)) { 347 wlan_objmgr_pdev_release_ref(pdev, WLAN_REGULATORY_NB_ID); 348 wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_NB_ID); 349 qdf_mem_free(payload); 350 } 351 352 return status; 353 } 354 355 QDF_STATUS reg_notify_sap_event(struct wlan_objmgr_pdev *pdev, 356 bool sap_state) 357 { 358 struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj; 359 struct wlan_regulatory_pdev_priv_obj *pdev_priv_obj; 360 struct wlan_objmgr_psoc *psoc; 361 QDF_STATUS status; 362 363 pdev_priv_obj = reg_get_pdev_obj(pdev); 364 365 if (!IS_VALID_PDEV_REG_OBJ(pdev_priv_obj)) { 366 reg_err("pdev reg component is NULL"); 367 return QDF_STATUS_E_INVAL; 368 } 369 370 psoc = wlan_pdev_get_psoc(pdev); 371 if (!psoc) { 372 reg_err("psoc is NULL"); 373 return QDF_STATUS_E_INVAL; 374 } 375 376 psoc_priv_obj = reg_get_psoc_obj(psoc); 377 if (!IS_VALID_PSOC_REG_OBJ(psoc_priv_obj)) { 378 reg_err("psoc reg component is NULL"); 379 return QDF_STATUS_E_INVAL; 380 } 381 382 reg_info("sap_state: %d", sap_state); 383 384 if (pdev_priv_obj->sap_state == sap_state) 385 return QDF_STATUS_SUCCESS; 386 387 pdev_priv_obj->sap_state = sap_state; 388 389 reg_compute_pdev_current_chan_list(pdev_priv_obj); 390 status = reg_send_scheduler_msg_sb(psoc, pdev); 391 392 return status; 393 } 394 395 void reg_register_chan_change_callback(struct wlan_objmgr_psoc *psoc, 396 reg_chan_change_callback cbk, void *arg) 397 { 398 struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj; 399 uint32_t count; 400 401 psoc_priv_obj = reg_get_psoc_obj(psoc); 402 if (!psoc_priv_obj) { 403 reg_err("reg psoc private obj is NULL"); 404 return; 405 } 406 407 qdf_spin_lock_bh(&psoc_priv_obj->cbk_list_lock); 408 for (count = 0; count < REG_MAX_CHAN_CHANGE_CBKS; count++) 409 if (!psoc_priv_obj->cbk_list[count].cbk) { 410 psoc_priv_obj->cbk_list[count].cbk = cbk; 411 psoc_priv_obj->cbk_list[count].arg = arg; 412 psoc_priv_obj->num_chan_change_cbks++; 413 break; 414 } 415 qdf_spin_unlock_bh(&psoc_priv_obj->cbk_list_lock); 416 417 if (count == REG_MAX_CHAN_CHANGE_CBKS) 418 reg_err("callback list is full"); 419 } 420 421 void reg_register_ctry_change_callback(struct wlan_objmgr_psoc *psoc, 422 reg_ctry_change_callback cbk) 423 { 424 struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj; 425 426 psoc_priv_obj = reg_get_psoc_obj(psoc); 427 if (!psoc_priv_obj) { 428 reg_err("reg psoc private obj is NULL"); 429 return; 430 } 431 432 qdf_spin_lock_bh(&psoc_priv_obj->cbk_list_lock); 433 if (!psoc_priv_obj->cc_cbk.cbk) 434 psoc_priv_obj->cc_cbk.cbk = cbk; 435 qdf_spin_unlock_bh(&psoc_priv_obj->cbk_list_lock); 436 } 437 438 void reg_unregister_chan_change_callback(struct wlan_objmgr_psoc *psoc, 439 reg_chan_change_callback cbk) 440 { 441 struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj; 442 uint32_t count; 443 444 psoc_priv_obj = reg_get_psoc_obj(psoc); 445 if (!psoc_priv_obj) { 446 reg_err("reg psoc private obj is NULL"); 447 return; 448 } 449 450 qdf_spin_lock_bh(&psoc_priv_obj->cbk_list_lock); 451 for (count = 0; count < REG_MAX_CHAN_CHANGE_CBKS; count++) 452 if (psoc_priv_obj->cbk_list[count].cbk == cbk) { 453 psoc_priv_obj->cbk_list[count].cbk = NULL; 454 psoc_priv_obj->num_chan_change_cbks--; 455 break; 456 } 457 qdf_spin_unlock_bh(&psoc_priv_obj->cbk_list_lock); 458 459 if (count == REG_MAX_CHAN_CHANGE_CBKS) 460 reg_err("callback not found in the list"); 461 } 462 463 void reg_unregister_ctry_change_callback(struct wlan_objmgr_psoc *psoc, 464 reg_ctry_change_callback cbk) 465 { 466 struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj; 467 468 psoc_priv_obj = reg_get_psoc_obj(psoc); 469 if (!psoc_priv_obj) { 470 reg_err("reg psoc private obj is NULL"); 471 return; 472 } 473 474 qdf_spin_lock_bh(&psoc_priv_obj->cbk_list_lock); 475 if (psoc_priv_obj->cc_cbk.cbk == cbk) 476 psoc_priv_obj->cc_cbk.cbk = NULL; 477 qdf_spin_unlock_bh(&psoc_priv_obj->cbk_list_lock); 478 } 479