1 /* 2 * Copyright (c) 2014-2021 The Linux Foundation. All rights reserved. 3 * Copyright (c) 2022-2023 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 * @payload: output pointer to allocated payload buffer 124 */ 125 static void reg_alloc_and_fill_payload(struct wlan_objmgr_psoc *psoc, 126 struct wlan_objmgr_pdev *pdev, 127 struct reg_sched_payload **payload) 128 { 129 struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj; 130 131 psoc_priv_obj = reg_get_psoc_obj(psoc); 132 if (!psoc_priv_obj) { 133 reg_err("reg psoc private obj is NULL"); 134 *payload = NULL; 135 return; 136 } 137 138 *payload = qdf_mem_malloc(sizeof(**payload)); 139 if (*payload) { 140 (*payload)->psoc = psoc; 141 (*payload)->pdev = pdev; 142 if (reg_check_coex_unsafe_nb_user_prefer(psoc)) { 143 reg_fill_freq_ext_payload(payload, psoc_priv_obj); 144 } else { 145 (*payload)->ch_avoid_ind = 146 !!psoc_priv_obj->ch_avoid_ind; 147 qdf_mem_copy(&(*payload)->avoid_info.freq_list, 148 &psoc_priv_obj->avoid_freq_list, 149 sizeof(psoc_priv_obj->avoid_freq_list)); 150 151 psoc_priv_obj->ch_avoid_ind = false; 152 } 153 qdf_mem_copy(&(*payload)->avoid_info.chan_list, 154 &psoc_priv_obj->unsafe_chan_list, 155 sizeof(psoc_priv_obj->unsafe_chan_list)); 156 } 157 } 158 159 /** 160 * reg_chan_change_flush_cbk_sb() - Flush south bound channel change callbacks. 161 * @msg: Pointer to scheduler msg structure. 162 */ 163 static QDF_STATUS reg_chan_change_flush_cbk_sb(struct scheduler_msg *msg) 164 { 165 struct reg_sched_payload *load = msg->bodyptr; 166 struct wlan_objmgr_psoc *psoc = load->psoc; 167 struct wlan_objmgr_pdev *pdev = load->pdev; 168 169 wlan_objmgr_pdev_release_ref(pdev, WLAN_REGULATORY_SB_ID); 170 wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_SB_ID); 171 qdf_mem_free(load); 172 173 return QDF_STATUS_SUCCESS; 174 } 175 176 /** 177 * reg_sched_chan_change_cbks_sb() - Schedule south bound channel change 178 * callbacks. 179 * @msg: Pointer to scheduler msg structure. 180 */ 181 static QDF_STATUS reg_sched_chan_change_cbks_sb(struct scheduler_msg *msg) 182 { 183 struct reg_sched_payload *load = msg->bodyptr; 184 struct wlan_objmgr_psoc *psoc = load->psoc; 185 struct wlan_objmgr_pdev *pdev = load->pdev; 186 187 reg_call_chan_change_cbks(psoc, pdev, load->ch_avoid_ind, 188 &load->avoid_info); 189 190 wlan_objmgr_pdev_release_ref(pdev, WLAN_REGULATORY_SB_ID); 191 wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_SB_ID); 192 qdf_mem_free(load); 193 194 return QDF_STATUS_SUCCESS; 195 } 196 197 /** 198 * reg_chan_change_flush_cbk_nb() - Flush north bound channel change callbacks. 199 * @msg: Pointer to scheduler msg structure. 200 */ 201 static QDF_STATUS reg_chan_change_flush_cbk_nb(struct scheduler_msg *msg) 202 { 203 struct reg_sched_payload *load = msg->bodyptr; 204 struct wlan_objmgr_psoc *psoc = load->psoc; 205 struct wlan_objmgr_pdev *pdev = load->pdev; 206 207 wlan_objmgr_pdev_release_ref(pdev, WLAN_REGULATORY_NB_ID); 208 wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_NB_ID); 209 qdf_mem_free(load); 210 211 return QDF_STATUS_SUCCESS; 212 } 213 214 /** 215 * reg_sched_chan_change_cbks_nb() - Schedule north bound channel change 216 * callbacks. 217 * @msg: Pointer to scheduler msg structure. 218 */ 219 static QDF_STATUS reg_sched_chan_change_cbks_nb(struct scheduler_msg *msg) 220 { 221 struct reg_sched_payload *load = msg->bodyptr; 222 struct wlan_objmgr_psoc *psoc = load->psoc; 223 struct wlan_objmgr_pdev *pdev = load->pdev; 224 225 reg_call_chan_change_cbks(psoc, pdev, load->ch_avoid_ind, 226 &load->avoid_info); 227 228 wlan_objmgr_pdev_release_ref(pdev, WLAN_REGULATORY_NB_ID); 229 wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_NB_ID); 230 qdf_mem_free(load); 231 232 return QDF_STATUS_SUCCESS; 233 } 234 235 QDF_STATUS reg_send_scheduler_msg_sb(struct wlan_objmgr_psoc *psoc, 236 struct wlan_objmgr_pdev *pdev) 237 { 238 struct scheduler_msg msg = {0}; 239 struct reg_sched_payload *payload; 240 struct wlan_regulatory_pdev_priv_obj *pdev_priv_obj; 241 QDF_STATUS status; 242 243 pdev_priv_obj = reg_get_pdev_obj(pdev); 244 if (!IS_VALID_PDEV_REG_OBJ(pdev_priv_obj)) { 245 reg_alert("pdev reg component is NULL"); 246 return QDF_STATUS_E_FAILURE; 247 } 248 249 if (!pdev_priv_obj->pdev_opened) { 250 reg_err("hlos not initialized"); 251 return QDF_STATUS_E_FAILURE; 252 } 253 254 if (!pdev_priv_obj->chan_list_recvd) { 255 reg_err("Empty channel list"); 256 return QDF_STATUS_E_FAILURE; 257 } 258 259 status = wlan_objmgr_psoc_try_get_ref(psoc, WLAN_REGULATORY_SB_ID); 260 if (QDF_IS_STATUS_ERROR(status)) { 261 reg_err("error taking psoc ref cnt"); 262 return status; 263 } 264 265 status = wlan_objmgr_pdev_try_get_ref(pdev, WLAN_REGULATORY_SB_ID); 266 if (QDF_IS_STATUS_ERROR(status)) { 267 wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_SB_ID); 268 reg_err("error taking pdev ref cnt"); 269 return status; 270 } 271 272 reg_alloc_and_fill_payload(psoc, pdev, &payload); 273 if (!payload) { 274 reg_err("malloc failed"); 275 wlan_objmgr_pdev_release_ref(pdev, WLAN_REGULATORY_SB_ID); 276 wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_SB_ID); 277 return QDF_STATUS_E_NOMEM; 278 } 279 280 msg.bodyptr = payload; 281 msg.callback = reg_sched_chan_change_cbks_sb; 282 msg.flush_callback = reg_chan_change_flush_cbk_sb; 283 284 status = scheduler_post_message(QDF_MODULE_ID_REGULATORY, 285 QDF_MODULE_ID_REGULATORY, 286 QDF_MODULE_ID_TARGET_IF, &msg); 287 if (QDF_IS_STATUS_ERROR(status)) { 288 wlan_objmgr_pdev_release_ref(pdev, WLAN_REGULATORY_SB_ID); 289 wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_SB_ID); 290 qdf_mem_free(payload); 291 } 292 293 return status; 294 } 295 296 QDF_STATUS reg_send_scheduler_msg_nb(struct wlan_objmgr_psoc *psoc, 297 struct wlan_objmgr_pdev *pdev) 298 { 299 struct scheduler_msg msg = {0}; 300 struct reg_sched_payload *payload; 301 struct wlan_regulatory_pdev_priv_obj *pdev_priv_obj; 302 QDF_STATUS status; 303 304 pdev_priv_obj = reg_get_pdev_obj(pdev); 305 if (!IS_VALID_PDEV_REG_OBJ(pdev_priv_obj)) { 306 reg_alert("pdev reg component is NULL"); 307 return QDF_STATUS_E_FAILURE; 308 } 309 310 if (!pdev_priv_obj->pdev_opened) { 311 reg_err("hlos not initialized"); 312 return QDF_STATUS_E_FAILURE; 313 } 314 315 if (!pdev_priv_obj->chan_list_recvd) { 316 reg_err("Empty channel list"); 317 return QDF_STATUS_E_FAILURE; 318 } 319 320 status = wlan_objmgr_psoc_try_get_ref(psoc, WLAN_REGULATORY_NB_ID); 321 if (QDF_IS_STATUS_ERROR(status)) { 322 reg_err("error taking psoc ref cnt"); 323 return status; 324 } 325 326 status = wlan_objmgr_pdev_try_get_ref(pdev, WLAN_REGULATORY_NB_ID); 327 if (QDF_IS_STATUS_ERROR(status)) { 328 wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_NB_ID); 329 reg_err("error taking pdev ref cnt"); 330 return status; 331 } 332 333 reg_alloc_and_fill_payload(psoc, pdev, &payload); 334 if (!payload) { 335 reg_err("malloc failed"); 336 wlan_objmgr_pdev_release_ref(pdev, WLAN_REGULATORY_NB_ID); 337 wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_NB_ID); 338 return QDF_STATUS_E_NOMEM; 339 } 340 msg.bodyptr = payload; 341 msg.callback = reg_sched_chan_change_cbks_nb; 342 msg.flush_callback = reg_chan_change_flush_cbk_nb; 343 344 status = scheduler_post_message(QDF_MODULE_ID_REGULATORY, 345 QDF_MODULE_ID_REGULATORY, 346 QDF_MODULE_ID_OS_IF, &msg); 347 if (QDF_IS_STATUS_ERROR(status)) { 348 wlan_objmgr_pdev_release_ref(pdev, WLAN_REGULATORY_NB_ID); 349 wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_NB_ID); 350 qdf_mem_free(payload); 351 } 352 353 return status; 354 } 355 356 QDF_STATUS reg_notify_sap_event(struct wlan_objmgr_pdev *pdev, 357 bool sap_state) 358 { 359 struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj; 360 struct wlan_regulatory_pdev_priv_obj *pdev_priv_obj; 361 struct wlan_objmgr_psoc *psoc; 362 QDF_STATUS status; 363 364 pdev_priv_obj = reg_get_pdev_obj(pdev); 365 366 if (!IS_VALID_PDEV_REG_OBJ(pdev_priv_obj)) { 367 reg_err("pdev reg component is NULL"); 368 return QDF_STATUS_E_INVAL; 369 } 370 371 psoc = wlan_pdev_get_psoc(pdev); 372 if (!psoc) { 373 reg_err("psoc is NULL"); 374 return QDF_STATUS_E_INVAL; 375 } 376 377 psoc_priv_obj = reg_get_psoc_obj(psoc); 378 if (!IS_VALID_PSOC_REG_OBJ(psoc_priv_obj)) { 379 reg_err("psoc reg component is NULL"); 380 return QDF_STATUS_E_INVAL; 381 } 382 383 reg_info("sap_state: %d", sap_state); 384 385 if (pdev_priv_obj->sap_state == sap_state) 386 return QDF_STATUS_SUCCESS; 387 388 pdev_priv_obj->sap_state = sap_state; 389 390 reg_compute_pdev_current_chan_list(pdev_priv_obj); 391 status = reg_send_scheduler_msg_sb(psoc, pdev); 392 393 return status; 394 } 395 396 void reg_register_chan_change_callback(struct wlan_objmgr_psoc *psoc, 397 reg_chan_change_callback cbk, void *arg) 398 { 399 struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj; 400 uint32_t count; 401 402 psoc_priv_obj = reg_get_psoc_obj(psoc); 403 if (!psoc_priv_obj) { 404 reg_err("reg psoc private obj is NULL"); 405 return; 406 } 407 408 qdf_spin_lock_bh(&psoc_priv_obj->cbk_list_lock); 409 for (count = 0; count < REG_MAX_CHAN_CHANGE_CBKS; count++) 410 if (!psoc_priv_obj->cbk_list[count].cbk) { 411 psoc_priv_obj->cbk_list[count].cbk = cbk; 412 psoc_priv_obj->cbk_list[count].arg = arg; 413 psoc_priv_obj->num_chan_change_cbks++; 414 break; 415 } 416 qdf_spin_unlock_bh(&psoc_priv_obj->cbk_list_lock); 417 418 if (count == REG_MAX_CHAN_CHANGE_CBKS) 419 reg_err("callback list is full"); 420 } 421 422 void reg_register_ctry_change_callback(struct wlan_objmgr_psoc *psoc, 423 reg_ctry_change_callback cbk) 424 { 425 struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj; 426 427 psoc_priv_obj = reg_get_psoc_obj(psoc); 428 if (!psoc_priv_obj) { 429 reg_err("reg psoc private obj is NULL"); 430 return; 431 } 432 433 qdf_spin_lock_bh(&psoc_priv_obj->cbk_list_lock); 434 if (!psoc_priv_obj->cc_cbk.cbk) 435 psoc_priv_obj->cc_cbk.cbk = cbk; 436 qdf_spin_unlock_bh(&psoc_priv_obj->cbk_list_lock); 437 } 438 439 void reg_unregister_chan_change_callback(struct wlan_objmgr_psoc *psoc, 440 reg_chan_change_callback cbk) 441 { 442 struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj; 443 uint32_t count; 444 445 psoc_priv_obj = reg_get_psoc_obj(psoc); 446 if (!psoc_priv_obj) { 447 reg_err("reg psoc private obj is NULL"); 448 return; 449 } 450 451 qdf_spin_lock_bh(&psoc_priv_obj->cbk_list_lock); 452 for (count = 0; count < REG_MAX_CHAN_CHANGE_CBKS; count++) 453 if (psoc_priv_obj->cbk_list[count].cbk == cbk) { 454 psoc_priv_obj->cbk_list[count].cbk = NULL; 455 psoc_priv_obj->num_chan_change_cbks--; 456 break; 457 } 458 qdf_spin_unlock_bh(&psoc_priv_obj->cbk_list_lock); 459 460 if (count == REG_MAX_CHAN_CHANGE_CBKS) 461 reg_err("callback not found in the list"); 462 } 463 464 void reg_unregister_ctry_change_callback(struct wlan_objmgr_psoc *psoc, 465 reg_ctry_change_callback cbk) 466 { 467 struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj; 468 469 psoc_priv_obj = reg_get_psoc_obj(psoc); 470 if (!psoc_priv_obj) { 471 reg_err("reg psoc private obj is NULL"); 472 return; 473 } 474 475 qdf_spin_lock_bh(&psoc_priv_obj->cbk_list_lock); 476 if (psoc_priv_obj->cc_cbk.cbk == cbk) 477 psoc_priv_obj->cc_cbk.cbk = NULL; 478 qdf_spin_unlock_bh(&psoc_priv_obj->cbk_list_lock); 479 } 480 481 void 482 reg_register_is_chan_connected_callback(struct wlan_objmgr_psoc *psoc, 483 reg_get_connected_chan_for_mode_callback cbk) 484 { 485 struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj; 486 487 psoc_priv_obj = reg_get_psoc_obj(psoc); 488 if (!psoc_priv_obj) { 489 reg_err("reg psoc private obj is NULL"); 490 return; 491 } 492 493 qdf_spin_lock_bh(&psoc_priv_obj->cbk_list_lock); 494 if (!psoc_priv_obj->conn_chan_cb.cbk) 495 psoc_priv_obj->conn_chan_cb.cbk = cbk; 496 qdf_spin_unlock_bh(&psoc_priv_obj->cbk_list_lock); 497 } 498 499 void 500 reg_unregister_is_chan_connected_callback(struct wlan_objmgr_psoc *psoc, 501 reg_get_connected_chan_for_mode_callback cbk) 502 { 503 struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj; 504 505 psoc_priv_obj = reg_get_psoc_obj(psoc); 506 if (!psoc_priv_obj) { 507 reg_err("reg psoc private obj is NULL"); 508 return; 509 } 510 511 qdf_spin_lock_bh(&psoc_priv_obj->cbk_list_lock); 512 if (psoc_priv_obj->conn_chan_cb.cbk == cbk) 513 psoc_priv_obj->conn_chan_cb.cbk = NULL; 514 qdf_spin_unlock_bh(&psoc_priv_obj->cbk_list_lock); 515 } 516