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