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