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