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