1 /* 2 * Copyright (c) 2018-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: Add 11d utility functions 21 */ 22 23 #include <wlan_cmn.h> 24 #include <reg_services_public_struct.h> 25 #include <wlan_scan_public_structs.h> 26 #include <wlan_scan_ucfg_api.h> 27 #include <wlan_objmgr_psoc_obj.h> 28 #include "reg_priv_objs.h" 29 #include "reg_utils.h" 30 #include "reg_services_common.h" 31 #include "reg_offload_11d_scan.h" 32 #include "reg_host_11d.h" 33 34 #ifdef TARGET_11D_SCAN 35 36 QDF_STATUS reg_set_11d_country(struct wlan_objmgr_pdev *pdev, 37 uint8_t *country) 38 { 39 struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj; 40 struct set_country country_code; 41 struct wlan_objmgr_psoc *psoc; 42 struct cc_regdmn_s rd; 43 QDF_STATUS status; 44 struct wlan_lmac_if_reg_tx_ops *tx_ops; 45 uint8_t pdev_id; 46 47 if (!country) { 48 reg_err("country code is NULL"); 49 return QDF_STATUS_E_INVAL; 50 } 51 52 pdev_id = wlan_objmgr_pdev_get_pdev_id(pdev); 53 54 psoc = wlan_pdev_get_psoc(pdev); 55 psoc_priv_obj = reg_get_psoc_obj(psoc); 56 if (!psoc_priv_obj) { 57 reg_err("psoc reg component is NULL"); 58 return QDF_STATUS_E_INVAL; 59 } 60 61 if (!qdf_mem_cmp(psoc_priv_obj->cur_country, country, REG_ALPHA2_LEN)) { 62 reg_debug("country is not different"); 63 return QDF_STATUS_SUCCESS; 64 } 65 66 reg_info("programming new 11d country:%c%c to firmware", 67 country[0], country[1]); 68 69 qdf_mem_copy(country_code.country, country, REG_ALPHA2_LEN + 1); 70 country_code.pdev_id = pdev_id; 71 72 psoc_priv_obj->new_11d_ctry_pending[pdev_id] = true; 73 74 if (psoc_priv_obj->offload_enabled) { 75 tx_ops = reg_get_psoc_tx_ops(psoc); 76 if (tx_ops->set_country_code) { 77 tx_ops->set_country_code(psoc, &country_code); 78 } else { 79 reg_err("country set fw handler not present"); 80 psoc_priv_obj->new_11d_ctry_pending[pdev_id] = false; 81 return QDF_STATUS_E_FAULT; 82 } 83 status = QDF_STATUS_SUCCESS; 84 } else { 85 qdf_mem_copy(rd.cc.alpha, country, REG_ALPHA2_LEN + 1); 86 rd.flags = ALPHA_IS_SET; 87 reg_program_chan_list(pdev, &rd); 88 status = QDF_STATUS_SUCCESS; 89 } 90 91 return status; 92 } 93 94 /** 95 * reg_send_11d_flush_cbk() - release 11d psoc reference 96 * @msg: Pointer to scheduler message. 97 * 98 * Return: QDF_STATUS 99 */ 100 static QDF_STATUS reg_send_11d_flush_cbk(struct scheduler_msg *msg) 101 { 102 struct wlan_objmgr_psoc *psoc = msg->bodyptr; 103 104 wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_SB_ID); 105 106 return QDF_STATUS_SUCCESS; 107 } 108 109 /** 110 * reg_send_11d_msg_cbk() - Send start/stop 11d scan message. 111 * @msg: Pointer to scheduler message. 112 * 113 * Return: QDF_STATUS 114 */ 115 static QDF_STATUS reg_send_11d_msg_cbk(struct scheduler_msg *msg) 116 { 117 struct wlan_objmgr_psoc *psoc = msg->bodyptr; 118 struct wlan_lmac_if_reg_tx_ops *tx_ops; 119 struct reg_start_11d_scan_req start_req; 120 struct reg_stop_11d_scan_req stop_req; 121 struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj; 122 123 tx_ops = reg_get_psoc_tx_ops(psoc); 124 125 psoc_priv_obj = reg_get_psoc_obj(psoc); 126 if (!psoc_priv_obj) { 127 reg_err("psoc priv obj is NULL"); 128 return QDF_STATUS_E_FAILURE; 129 } 130 131 if (psoc_priv_obj->enable_11d_supp) { 132 start_req.vdev_id = psoc_priv_obj->vdev_id_for_11d_scan; 133 start_req.scan_period_msec = psoc_priv_obj->scan_11d_interval; 134 start_req.start_interval_msec = 0; 135 reg_debug("sending start msg"); 136 tx_ops->start_11d_scan(psoc, &start_req); 137 } else { 138 stop_req.vdev_id = psoc_priv_obj->vdev_id_for_11d_scan; 139 reg_debug("sending stop msg"); 140 tx_ops->stop_11d_scan(psoc, &stop_req); 141 } 142 143 wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_SB_ID); 144 return QDF_STATUS_SUCCESS; 145 } 146 147 /** 148 * reg_sched_11d_msg() - Schedules 11d scan message. 149 * @psoc: soc context 150 */ 151 static QDF_STATUS reg_sched_11d_msg(struct wlan_objmgr_psoc *psoc) 152 { 153 struct scheduler_msg msg = {0}; 154 QDF_STATUS status; 155 156 status = wlan_objmgr_psoc_try_get_ref(psoc, WLAN_REGULATORY_SB_ID); 157 if (QDF_IS_STATUS_ERROR(status)) { 158 reg_err("error taking psoc ref cnt"); 159 return status; 160 } 161 162 msg.bodyptr = psoc; 163 msg.callback = reg_send_11d_msg_cbk; 164 msg.flush_callback = reg_send_11d_flush_cbk; 165 166 status = scheduler_post_message(QDF_MODULE_ID_REGULATORY, 167 QDF_MODULE_ID_REGULATORY, 168 QDF_MODULE_ID_TARGET_IF, &msg); 169 if (QDF_IS_STATUS_ERROR(status)) { 170 wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_SB_ID); 171 reg_err("scheduler msg posting failed"); 172 } 173 174 return status; 175 } 176 177 void reg_run_11d_state_machine(struct wlan_objmgr_psoc *psoc) 178 { 179 bool temp_11d_support; 180 struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj; 181 bool world_mode; 182 183 psoc_priv_obj = reg_get_psoc_obj(psoc); 184 if (!psoc_priv_obj) { 185 reg_err("reg psoc private obj is NULL"); 186 return; 187 } 188 189 world_mode = reg_is_world_alpha2(psoc_priv_obj->cur_country); 190 191 temp_11d_support = psoc_priv_obj->enable_11d_supp; 192 if ((psoc_priv_obj->enable_11d_in_world_mode) && (world_mode)) 193 psoc_priv_obj->enable_11d_supp = true; 194 else if (((psoc_priv_obj->user_ctry_set) && 195 (psoc_priv_obj->user_ctry_priority)) || 196 (psoc_priv_obj->master_vdev_cnt)) 197 psoc_priv_obj->enable_11d_supp = false; 198 else 199 psoc_priv_obj->enable_11d_supp = 200 psoc_priv_obj->enable_11d_supp_original; 201 202 reg_debug("inside 11d state machine:tmp %d 11d_supp %d org %d set %d pri %d cnt %d vdev %d", 203 temp_11d_support, 204 psoc_priv_obj->enable_11d_supp, 205 psoc_priv_obj->enable_11d_supp_original, 206 psoc_priv_obj->user_ctry_set, 207 psoc_priv_obj->user_ctry_priority, 208 psoc_priv_obj->master_vdev_cnt, 209 psoc_priv_obj->vdev_id_for_11d_scan); 210 211 if (temp_11d_support != psoc_priv_obj->enable_11d_supp) { 212 if (psoc_priv_obj->is_11d_offloaded) 213 reg_sched_11d_msg(psoc); 214 else 215 reg_11d_host_scan(psoc_priv_obj); 216 } 217 } 218 219 QDF_STATUS reg_11d_vdev_created_update(struct wlan_objmgr_vdev *vdev) 220 { 221 struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj; 222 struct wlan_objmgr_pdev *parent_pdev; 223 struct wlan_objmgr_psoc *parent_psoc; 224 uint32_t vdev_id; 225 enum QDF_OPMODE op_mode; 226 uint8_t i; 227 228 op_mode = wlan_vdev_mlme_get_opmode(vdev); 229 230 parent_pdev = wlan_vdev_get_pdev(vdev); 231 parent_psoc = wlan_pdev_get_psoc(parent_pdev); 232 233 psoc_priv_obj = reg_get_psoc_obj(parent_psoc); 234 if (!psoc_priv_obj) { 235 reg_err("reg psoc private obj is NULL"); 236 return QDF_STATUS_E_FAULT; 237 } 238 239 if ((op_mode == QDF_STA_MODE) || 240 (op_mode == QDF_P2P_DEVICE_MODE) || 241 (op_mode == QDF_P2P_CLIENT_MODE)) { 242 vdev_id = wlan_vdev_get_id(vdev); 243 if (!psoc_priv_obj->vdev_cnt_11d) { 244 psoc_priv_obj->vdev_id_for_11d_scan = vdev_id; 245 reg_debug("running 11d state machine, opmode %d", 246 op_mode); 247 reg_run_11d_state_machine(parent_psoc); 248 } 249 250 for (i = 0; i < MAX_STA_VDEV_CNT; i++) { 251 if (psoc_priv_obj->vdev_ids_11d[i] == INVALID_VDEV_ID) { 252 psoc_priv_obj->vdev_ids_11d[i] = vdev_id; 253 break; 254 } 255 } 256 psoc_priv_obj->vdev_cnt_11d++; 257 } 258 259 if ((op_mode == QDF_P2P_GO_MODE) || (op_mode == QDF_SAP_MODE)) { 260 reg_debug("running 11d state machine, opmode %d", op_mode); 261 psoc_priv_obj->master_vdev_cnt++; 262 reg_run_11d_state_machine(parent_psoc); 263 } 264 265 return QDF_STATUS_SUCCESS; 266 } 267 268 QDF_STATUS reg_11d_vdev_delete_update(struct wlan_objmgr_vdev *vdev) 269 { 270 struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj; 271 struct wlan_objmgr_pdev *parent_pdev; 272 struct wlan_objmgr_psoc *parent_psoc; 273 enum QDF_OPMODE op_mode; 274 uint32_t vdev_id; 275 uint8_t i; 276 277 if (!vdev) { 278 reg_err("vdev is NULL"); 279 return QDF_STATUS_E_INVAL; 280 } 281 op_mode = wlan_vdev_mlme_get_opmode(vdev); 282 283 parent_pdev = wlan_vdev_get_pdev(vdev); 284 parent_psoc = wlan_pdev_get_psoc(parent_pdev); 285 286 psoc_priv_obj = reg_get_psoc_obj(parent_psoc); 287 if (!psoc_priv_obj) { 288 reg_err("reg psoc private obj is NULL"); 289 return QDF_STATUS_E_FAULT; 290 } 291 292 if ((op_mode == QDF_P2P_GO_MODE) || (op_mode == QDF_SAP_MODE)) { 293 psoc_priv_obj->master_vdev_cnt--; 294 reg_debug("run 11d state machine, deleted opmode %d", 295 op_mode); 296 reg_run_11d_state_machine(parent_psoc); 297 return QDF_STATUS_SUCCESS; 298 } 299 300 if ((op_mode == QDF_STA_MODE) || (op_mode == QDF_P2P_DEVICE_MODE) || 301 (op_mode == QDF_P2P_CLIENT_MODE)) { 302 vdev_id = wlan_vdev_get_id(vdev); 303 for (i = 0; i < MAX_STA_VDEV_CNT; i++) { 304 if (psoc_priv_obj->vdev_ids_11d[i] == vdev_id) { 305 psoc_priv_obj->vdev_ids_11d[i] = 306 INVALID_VDEV_ID; 307 psoc_priv_obj->vdev_cnt_11d--; 308 break; 309 } 310 } 311 312 if ((psoc_priv_obj->vdev_id_for_11d_scan != vdev_id) || 313 !psoc_priv_obj->vdev_cnt_11d) 314 return QDF_STATUS_SUCCESS; 315 316 for (i = 0; i < MAX_STA_VDEV_CNT; i++) { 317 if (psoc_priv_obj->vdev_ids_11d[i] == INVALID_VDEV_ID) 318 continue; 319 psoc_priv_obj->vdev_id_for_11d_scan = 320 psoc_priv_obj->vdev_ids_11d[i]; 321 psoc_priv_obj->enable_11d_supp = false; 322 reg_debug("running 11d state machine, vdev %d", 323 psoc_priv_obj->vdev_id_for_11d_scan); 324 reg_run_11d_state_machine(parent_psoc); 325 break; 326 } 327 } 328 329 return QDF_STATUS_SUCCESS; 330 } 331 332 bool reg_is_11d_scan_inprogress(struct wlan_objmgr_psoc *psoc) 333 { 334 struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj; 335 336 psoc_priv_obj = reg_get_psoc_obj(psoc); 337 if (!psoc_priv_obj) { 338 reg_err("reg psoc private obj is NULL"); 339 return false; 340 } 341 342 return psoc_priv_obj->enable_11d_supp; 343 } 344 345 QDF_STATUS reg_save_new_11d_country(struct wlan_objmgr_psoc *psoc, 346 uint8_t *country) 347 { 348 struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj; 349 struct wlan_lmac_if_reg_tx_ops *tx_ops; 350 struct set_country country_code; 351 uint8_t pdev_id; 352 353 psoc_priv_obj = reg_get_psoc_obj(psoc); 354 if (!psoc_priv_obj) { 355 reg_err("reg psoc private obj is NULL"); 356 357 return QDF_STATUS_E_FAILURE; 358 } 359 360 pdev_id = psoc_priv_obj->def_pdev_id; 361 psoc_priv_obj->new_11d_ctry_pending[pdev_id] = true; 362 qdf_mem_copy(country_code.country, country, REG_ALPHA2_LEN + 1); 363 country_code.pdev_id = pdev_id; 364 365 if (psoc_priv_obj->offload_enabled) { 366 tx_ops = reg_get_psoc_tx_ops(psoc); 367 if (tx_ops->set_country_code) { 368 tx_ops->set_country_code(psoc, &country_code); 369 } else { 370 reg_err("country set handler is not present"); 371 psoc_priv_obj->new_11d_ctry_pending[pdev_id] = false; 372 return QDF_STATUS_E_FAULT; 373 } 374 } 375 376 return QDF_STATUS_SUCCESS; 377 } 378 379 bool reg_11d_enabled_on_host(struct wlan_objmgr_psoc *psoc) 380 { 381 struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj; 382 383 psoc_priv_obj = reg_get_psoc_obj(psoc); 384 if (!psoc_priv_obj) { 385 reg_err("reg psoc private obj is NULL"); 386 return QDF_STATUS_E_FAILURE; 387 } 388 389 return (psoc_priv_obj->enable_11d_supp && 390 !psoc_priv_obj->is_11d_offloaded); 391 } 392 393 QDF_STATUS reg_set_11d_offloaded(struct wlan_objmgr_psoc *psoc, bool val) 394 { 395 struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj; 396 397 psoc_priv_obj = reg_get_psoc_obj(psoc); 398 if (!psoc_priv_obj) { 399 reg_err("psoc reg component is NULL"); 400 return QDF_STATUS_E_FAILURE; 401 } 402 403 psoc_priv_obj->is_11d_offloaded = val; 404 reg_debug("set is_11d_offloaded %d", val); 405 return QDF_STATUS_SUCCESS; 406 } 407 408 bool reg_is_11d_offloaded(struct wlan_objmgr_psoc *psoc) 409 { 410 struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj; 411 412 psoc_priv_obj = reg_get_psoc_obj(psoc); 413 if (!psoc_priv_obj) { 414 reg_err("reg psoc private obj is NULL"); 415 return false; 416 } 417 418 return psoc_priv_obj->is_11d_offloaded; 419 } 420 #endif 421