1 /* 2 * Copyright (c) 2012-2021 The Linux Foundation. All rights reserved. 3 * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved. 4 * 5 * Permission to use, copy, modify, and/or distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include "osif_vdev_sync.h" 19 #include "wlan_hdd_hostapd.h" 20 #include "wlan_hdd_pre_cac.h" 21 #include <qdf_types.h> 22 #include "osif_pre_cac.h" 23 #include "wlan_pre_cac_ucfg_api.h" 24 #include "wlan_ipa_ucfg_api.h" 25 #include "wlan_hdd_son.h" 26 #include "wlan_dp_ucfg_api.h" 27 28 /** 29 * wlan_hdd_pre_cac_failure() - Process the pre cac failure 30 * @adapter: AP adapter 31 * 32 * Deletes the pre cac adapter 33 * 34 * Return: None 35 */ 36 static void wlan_hdd_pre_cac_failure(struct hdd_adapter *adapter) 37 { 38 struct hdd_context *hdd_ctx; 39 40 hdd_enter(); 41 42 hdd_ctx = WLAN_HDD_GET_CTX(adapter); 43 if (wlan_hdd_validate_context(hdd_ctx)) 44 return; 45 46 wlan_hdd_stop_sap(adapter); 47 hdd_stop_adapter(hdd_ctx, adapter); 48 49 hdd_exit(); 50 } 51 52 /** 53 * wlan_hdd_pre_cac_success() - Process the pre cac result 54 * @adapter: AP adapter 55 * 56 * Stops the pre cac adapter and moves the existing SAP to the pre cac 57 * channel 58 * 59 * Return: None 60 */ 61 static void wlan_hdd_pre_cac_success(struct hdd_adapter *adapter) 62 { 63 struct hdd_adapter *ap_adapter; 64 int i; 65 struct hdd_context *hdd_ctx; 66 enum phy_ch_width pre_cac_ch_width; 67 qdf_freq_t chan_freq; 68 69 hdd_enter(); 70 71 hdd_ctx = WLAN_HDD_GET_CTX(adapter); 72 if (!hdd_ctx) { 73 hdd_err("HDD context is null"); 74 return; 75 } 76 77 pre_cac_ch_width = wlansap_get_chan_width( 78 WLAN_HDD_GET_SAP_CTX_PTR(adapter->deflink)); 79 80 hdd_stop_adapter(hdd_ctx, adapter); 81 82 /* Prepare to switch AP from 2.4GHz channel to the pre CAC channel */ 83 ap_adapter = hdd_get_adapter(hdd_ctx, QDF_SAP_MODE); 84 if (!ap_adapter) { 85 hdd_err("failed to get SAP adapter, no restart on pre CAC channel"); 86 return; 87 } 88 89 /* 90 * Setting of the pre cac complete status will ensure that on channel 91 * switch to the pre CAC DFS channel, there is no CAC again. 92 */ 93 ucfg_pre_cac_complete_set(ap_adapter->deflink->vdev, true); 94 95 wlan_hdd_set_sap_csa_reason(hdd_ctx->psoc, ap_adapter->deflink->vdev_id, 96 CSA_REASON_PRE_CAC_SUCCESS); 97 chan_freq = ucfg_pre_cac_get_freq(ap_adapter->deflink->vdev); 98 i = hdd_softap_set_channel_change(ap_adapter->dev, 99 chan_freq, 100 pre_cac_ch_width, false); 101 if (i) { 102 hdd_err("failed to change channel"); 103 ucfg_pre_cac_complete_set(ap_adapter->deflink->vdev, false); 104 } 105 106 hdd_exit(); 107 } 108 109 void hdd_close_pre_cac_adapter(struct hdd_context *hdd_ctx) 110 { 111 struct hdd_adapter *pre_cac_adapter; 112 struct osif_vdev_sync *vdev_sync; 113 int errno; 114 115 pre_cac_adapter = hdd_get_adapter_by_iface_name(hdd_ctx, 116 SAP_PRE_CAC_IFNAME); 117 if (!pre_cac_adapter) 118 return; 119 120 ucfg_pre_cac_clear_work(hdd_ctx->psoc); 121 errno = osif_vdev_sync_trans_start_wait(pre_cac_adapter->dev, 122 &vdev_sync); 123 if (errno) 124 return; 125 126 osif_vdev_sync_unregister(pre_cac_adapter->dev); 127 osif_vdev_sync_wait_for_ops(vdev_sync); 128 129 wlan_hdd_release_intf_addr(hdd_ctx, pre_cac_adapter->mac_addr.bytes); 130 pre_cac_adapter->is_virtual_iface = true; 131 hdd_close_adapter(hdd_ctx, pre_cac_adapter, true); 132 133 osif_vdev_sync_trans_stop(vdev_sync); 134 osif_vdev_sync_destroy(vdev_sync); 135 } 136 137 static int wlan_set_def_pre_cac_chan(struct hdd_context *hdd_ctx, 138 uint32_t pre_cac_ch_freq, 139 struct cfg80211_chan_def *chandef, 140 enum nl80211_channel_type *chantype, 141 enum phy_ch_width *ch_width) 142 { 143 enum nl80211_channel_type channel_type; 144 struct ieee80211_channel *ieee_chan; 145 struct ch_params ch_params = {0}; 146 147 ieee_chan = ieee80211_get_channel(hdd_ctx->wiphy, 148 pre_cac_ch_freq); 149 if (!ieee_chan) { 150 hdd_err("channel conversion failed %d", pre_cac_ch_freq); 151 return -EINVAL; 152 } 153 ch_params.ch_width = *ch_width; 154 wlan_reg_set_channel_params_for_pwrmode(hdd_ctx->pdev, 155 pre_cac_ch_freq, 0, 156 &ch_params, 157 REG_CURRENT_PWR_MODE); 158 switch (ch_params.sec_ch_offset) { 159 case HIGH_PRIMARY_CH: 160 channel_type = NL80211_CHAN_HT40MINUS; 161 break; 162 case LOW_PRIMARY_CH: 163 channel_type = NL80211_CHAN_HT40PLUS; 164 break; 165 default: 166 channel_type = NL80211_CHAN_HT20; 167 break; 168 } 169 cfg80211_chandef_create(chandef, ieee_chan, channel_type); 170 switch (ch_params.ch_width) { 171 case CH_WIDTH_80MHZ: 172 chandef->width = NL80211_CHAN_WIDTH_80; 173 break; 174 case CH_WIDTH_80P80MHZ: 175 chandef->width = NL80211_CHAN_WIDTH_80P80; 176 if (ch_params.mhz_freq_seg1) 177 chandef->center_freq2 = ch_params.mhz_freq_seg1; 178 break; 179 case CH_WIDTH_160MHZ: 180 chandef->width = NL80211_CHAN_WIDTH_160; 181 break; 182 default: 183 break; 184 } 185 if (ch_params.ch_width == CH_WIDTH_80MHZ || 186 ch_params.ch_width == CH_WIDTH_80P80MHZ || 187 ch_params.ch_width == CH_WIDTH_160MHZ) { 188 if (ch_params.mhz_freq_seg0) 189 chandef->center_freq1 = ch_params.mhz_freq_seg0; 190 } 191 *chantype = channel_type; 192 *ch_width = ch_params.ch_width; 193 hdd_debug("pre cac ch def: chan:%d width:%d freq1:%d freq2:%d", 194 chandef->chan->center_freq, chandef->width, 195 chandef->center_freq1, chandef->center_freq2); 196 197 return 0; 198 } 199 200 /** 201 * __wlan_hdd_request_pre_cac() - Start pre CAC in the driver 202 * @hdd_ctx: the HDD context to operate against 203 * @chan_freq: Channel frequency option provided by userspace 204 * @out_adapter: out parameter for the newly created pre-cac adapter 205 * 206 * Sets the driver to the required hardware mode and start an adapter for 207 * pre CAC which will mimic an AP. 208 * 209 * Return: Zero on success, non-zero value on error 210 */ 211 static int __wlan_hdd_request_pre_cac(struct hdd_context *hdd_ctx, 212 uint32_t chan_freq, 213 struct hdd_adapter **out_adapter) 214 { 215 uint8_t *mac_addr = NULL; 216 uint32_t pre_cac_chan_freq = 0; 217 int ret; 218 struct hdd_adapter *ap_adapter, *pre_cac_adapter; 219 struct hdd_ap_ctx *hdd_ap_ctx, *pre_cac_ap_ctx; 220 QDF_STATUS status; 221 struct wiphy *wiphy; 222 struct net_device *dev; 223 struct cfg80211_chan_def chandef; 224 enum nl80211_channel_type channel_type; 225 mac_handle_t mac_handle; 226 enum phy_ch_width cac_ch_width; 227 struct hdd_adapter_create_param params = {0}; 228 struct wlan_hdd_link_info *pre_cac_link_info, *link_info; 229 230 if (!policy_mgr_is_hw_dbs_capable(hdd_ctx->psoc)) { 231 hdd_debug("Pre CAC is not supported on non-dbs platforms"); 232 return -EINVAL; 233 } 234 235 if (policy_mgr_get_connection_count(hdd_ctx->psoc) > 1) { 236 hdd_err("pre cac not allowed in concurrency"); 237 return -EINVAL; 238 } 239 240 ap_adapter = hdd_get_adapter(hdd_ctx, QDF_SAP_MODE); 241 if (!ap_adapter) { 242 hdd_err("unable to get SAP adapter"); 243 return -EINVAL; 244 } 245 246 link_info = ap_adapter->deflink; 247 hdd_ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(link_info); 248 249 if (qdf_atomic_read(&hdd_ap_ctx->ch_switch_in_progress)) { 250 hdd_err("pre cac not allowed during CSA"); 251 return -EINVAL; 252 } 253 254 mac_handle = hdd_ctx->mac_handle; 255 256 if (wlan_reg_is_dfs_for_freq(hdd_ctx->pdev, 257 hdd_ap_ctx->operating_chan_freq)) { 258 hdd_err("SAP is already on DFS channel:%d", 259 hdd_ap_ctx->operating_chan_freq); 260 return -EINVAL; 261 } 262 263 if (!WLAN_REG_IS_24GHZ_CH_FREQ(hdd_ap_ctx->operating_chan_freq)) { 264 hdd_err("pre CAC allowed only when SAP is in 2.4GHz:%d", 265 hdd_ap_ctx->operating_chan_freq); 266 return -EINVAL; 267 } 268 269 hdd_debug("channel: %d", chan_freq); 270 271 ret = ucfg_pre_cac_validate_and_get_freq(hdd_ctx->pdev, chan_freq, 272 &pre_cac_chan_freq); 273 if (ret != 0) { 274 hdd_err("can't validate pre-cac channel"); 275 goto release_intf_addr_and_return_failure; 276 } 277 278 hdd_debug("starting pre cac SAP adapter"); 279 280 mac_addr = wlan_hdd_get_intf_addr(hdd_ctx, QDF_SAP_MODE); 281 if (!mac_addr) { 282 hdd_err("can't add virtual intf: Not getting valid mac addr"); 283 return -EINVAL; 284 } 285 286 /** 287 * Starting a SAP adapter: 288 * Instead of opening an adapter, we could just do a SME open 289 * session for AP type. But, start BSS would still need an 290 * adapter. So, this option is not taken. 291 * 292 * hdd open adapter is going to register this precac interface 293 * with user space. This interface though exposed to user space 294 * will be in DOWN state. Consideration was done to avoid this 295 * registration to the user space. But, as part of SAP 296 * operations multiple events are sent to user space. Some of 297 * these events received from unregistered interface was 298 * causing crashes. So, retaining the registration. 299 * 300 * So, this interface would remain registered and will remain 301 * in DOWN state for the CAC duration. We will add notes in the 302 * feature announcement to not use this temporary interface for 303 * any activity from user space. 304 */ 305 params.is_add_virtual_iface = 1; 306 pre_cac_adapter = hdd_open_adapter(hdd_ctx, QDF_SAP_MODE, 307 SAP_PRE_CAC_IFNAME, mac_addr, 308 NET_NAME_UNKNOWN, true, 309 ¶ms); 310 311 if (!pre_cac_adapter) { 312 hdd_err("error opening the pre cac adapter"); 313 goto release_intf_addr_and_return_failure; 314 } 315 316 pre_cac_link_info = pre_cac_adapter->deflink; 317 pre_cac_ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(pre_cac_link_info); 318 sap_clear_global_dfs_param(mac_handle, pre_cac_ap_ctx->sap_context); 319 320 /* 321 * This interface is internally created by the driver. So, no interface 322 * up comes for this interface from user space and hence starting 323 * the adapter internally. 324 */ 325 if (hdd_start_adapter(pre_cac_adapter, false)) { 326 hdd_err("error starting the pre cac adapter"); 327 goto close_pre_cac_adapter; 328 } 329 330 hdd_debug("preparing for start ap/bss on the pre cac adapter"); 331 332 wiphy = hdd_ctx->wiphy; 333 dev = pre_cac_adapter->dev; 334 335 /* Since this is only a dummy interface lets us use the IEs from the 336 * other active SAP interface. In regular scenarios, these IEs would 337 * come from the user space entity 338 */ 339 pre_cac_ap_ctx->beacon = qdf_mem_malloc(sizeof(*hdd_ap_ctx->beacon)); 340 if (!pre_cac_ap_ctx->beacon) 341 goto stop_close_pre_cac_adapter; 342 343 qdf_mem_copy(pre_cac_ap_ctx->beacon, hdd_ap_ctx->beacon, 344 sizeof(*pre_cac_ap_ctx->beacon)); 345 pre_cac_ap_ctx->sap_config.authType = hdd_ap_ctx->sap_config.authType; 346 pre_cac_ap_ctx->sap_config.ch_width_orig = 347 hdd_ap_ctx->sap_config.ch_width_orig; 348 349 /* The original premise is that on moving from 2.4GHz to 5GHz, the SAP 350 * will continue to operate on the same bandwidth as that of the 2.4GHz 351 * operations. Only bandwidths 20MHz/40MHz are possible on 2.4GHz band. 352 * Now some customer request to start AP on higher BW such as 80Mhz. 353 * Hence use max possible supported BW based on phymode configurated 354 * on SAP. 355 */ 356 cac_ch_width = wlansap_get_max_bw_by_phymode(hdd_ap_ctx->sap_context); 357 if (cac_ch_width > DEFAULT_PRE_CAC_BANDWIDTH) 358 cac_ch_width = DEFAULT_PRE_CAC_BANDWIDTH; 359 360 qdf_mem_zero(&chandef, sizeof(struct cfg80211_chan_def)); 361 if (wlan_set_def_pre_cac_chan(hdd_ctx, pre_cac_chan_freq, &chandef, 362 &channel_type, &cac_ch_width)) { 363 hdd_err("error set pre_cac channel %d", pre_cac_chan_freq); 364 goto close_pre_cac_adapter; 365 } 366 pre_cac_ap_ctx->sap_config.ch_width_orig = 367 hdd_map_nl_chan_width(chandef.width); 368 369 hdd_debug("existing ap phymode:%d pre cac ch_width:%d freq:%d", 370 hdd_ap_ctx->sap_config.SapHw_mode, 371 cac_ch_width, pre_cac_chan_freq); 372 /* 373 * Doing update after opening and starting pre-cac adapter will make 374 * sure that driver won't do hardware mode change if there are any 375 * initial hick-ups or issues in pre-cac adapter's configuration. 376 * Since current SAP is in 2.4GHz and pre CAC channel is in 5GHz, this 377 * connection update should result in DBS mode 378 */ 379 status = policy_mgr_update_and_wait_for_connection_update( 380 hdd_ctx->psoc, 381 link_info->vdev_id, 382 pre_cac_chan_freq, 383 POLICY_MGR_UPDATE_REASON_PRE_CAC); 384 if (QDF_IS_STATUS_ERROR(status)) { 385 hdd_err("error in moving to DBS mode"); 386 goto stop_close_pre_cac_adapter; 387 } 388 389 ret = wlan_hdd_set_channel(wiphy, dev, &chandef, channel_type); 390 if (ret != 0) { 391 hdd_err("failed to set channel"); 392 goto stop_close_pre_cac_adapter; 393 } 394 395 status = wlan_hdd_cfg80211_start_bss(pre_cac_link_info, 396 NULL, PRE_CAC_SSID, 397 qdf_str_len(PRE_CAC_SSID), 398 NL80211_HIDDEN_SSID_NOT_IN_USE, 399 false); 400 if (QDF_IS_STATUS_ERROR(status)) { 401 hdd_err("start bss failed"); 402 goto stop_close_pre_cac_adapter; 403 } 404 405 /* 406 * The pre cac status is set here. But, it would not be reset explicitly 407 * anywhere, since after the pre cac success/failure, the pre cac 408 * adapter itself would be removed. 409 */ 410 ret = ucfg_pre_cac_set_status(pre_cac_link_info->vdev, true); 411 if (ret != 0) { 412 hdd_err("failed to set pre cac status"); 413 goto stop_close_pre_cac_adapter; 414 } 415 416 ucfg_pre_cac_set_freq_before_pre_cac(link_info->vdev, 417 hdd_ap_ctx->operating_chan_freq); 418 ucfg_pre_cac_set_freq(link_info->vdev, pre_cac_chan_freq); 419 ucfg_pre_cac_adapter_set(pre_cac_link_info->vdev, true); 420 *out_adapter = pre_cac_adapter; 421 422 return 0; 423 424 stop_close_pre_cac_adapter: 425 pre_cac_adapter->is_virtual_iface = true; 426 hdd_stop_adapter(hdd_ctx, pre_cac_adapter); 427 qdf_mem_free(pre_cac_ap_ctx->beacon); 428 pre_cac_ap_ctx->beacon = NULL; 429 close_pre_cac_adapter: 430 hdd_close_adapter(hdd_ctx, pre_cac_adapter, true); 431 release_intf_addr_and_return_failure: 432 /* 433 * Release the interface address as the adapter 434 * failed to start, if you don't release then next 435 * adapter which is trying to come wouldn't get valid 436 * mac address. Remember we have limited pool of mac addresses 437 */ 438 if (mac_addr) 439 wlan_hdd_release_intf_addr(hdd_ctx, mac_addr); 440 return -EINVAL; 441 } 442 443 static int 444 wlan_hdd_start_pre_cac_trans(struct hdd_context *hdd_ctx, 445 struct osif_vdev_sync **out_vdev_sync, 446 bool *is_vdev_sync_created) 447 { 448 struct hdd_adapter *adapter, *next_adapter = NULL; 449 wlan_net_dev_ref_dbgid dbgid = NET_DEV_HOLD_START_PRE_CAC_TRANS; 450 int errno; 451 452 hdd_for_each_adapter_dev_held_safe(hdd_ctx, adapter, next_adapter, 453 dbgid) { 454 if (!qdf_str_cmp(adapter->dev->name, SAP_PRE_CAC_IFNAME)) { 455 errno = osif_vdev_sync_trans_start(adapter->dev, 456 out_vdev_sync); 457 458 hdd_adapter_dev_put_debug(adapter, dbgid); 459 if (next_adapter) 460 hdd_adapter_dev_put_debug(next_adapter, 461 dbgid); 462 return errno; 463 } 464 hdd_adapter_dev_put_debug(adapter, dbgid); 465 } 466 467 errno = osif_vdev_sync_create_and_trans(hdd_ctx->parent_dev, 468 out_vdev_sync); 469 if (errno) 470 return errno; 471 472 *is_vdev_sync_created = true; 473 return 0; 474 } 475 476 int wlan_hdd_request_pre_cac(struct hdd_context *hdd_ctx, uint32_t chan_freq) 477 { 478 struct hdd_adapter *adapter; 479 struct osif_vdev_sync *vdev_sync; 480 int errno; 481 bool is_vdev_sync_created = false; 482 483 errno = wlan_hdd_start_pre_cac_trans(hdd_ctx, &vdev_sync, 484 &is_vdev_sync_created); 485 if (errno) 486 return errno; 487 488 errno = __wlan_hdd_request_pre_cac(hdd_ctx, chan_freq, &adapter); 489 if (errno) 490 goto destroy_sync; 491 492 if (is_vdev_sync_created) 493 osif_vdev_sync_register(adapter->dev, vdev_sync); 494 osif_vdev_sync_trans_stop(vdev_sync); 495 496 return 0; 497 498 destroy_sync: 499 osif_vdev_sync_trans_stop(vdev_sync); 500 if (is_vdev_sync_created) 501 osif_vdev_sync_destroy(vdev_sync); 502 503 return errno; 504 } 505 506 static void 507 wlan_hdd_pre_cac_conditional_freq_switch_ind(struct wlan_objmgr_vdev *vdev, 508 bool completed) 509 { 510 struct wlan_objmgr_psoc *psoc = wlan_vdev_get_psoc(vdev); 511 uint8_t vdev_id = vdev->vdev_objmgr.vdev_id; 512 struct hdd_adapter *adapter; 513 struct wlan_hdd_link_info *link_info; 514 struct hdd_ap_ctx *ap_ctx; 515 516 link_info = wlan_hdd_get_link_info_from_vdev(psoc, vdev_id); 517 if (!link_info) { 518 hdd_err("Invalid vdev"); 519 return; 520 } 521 522 adapter = link_info->adapter; 523 if (completed) { 524 ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(link_info); 525 ap_ctx->dfs_cac_block_tx = false; 526 ucfg_ipa_set_dfs_cac_tx(adapter->hdd_ctx->pdev, 527 ap_ctx->dfs_cac_block_tx); 528 ucfg_dp_set_dfs_cac_tx(vdev, ap_ctx->dfs_cac_block_tx); 529 adapter->hdd_ctx->dev_dfs_cac_status = DFS_CAC_ALREADY_DONE; 530 } else { 531 adapter->hdd_ctx->dev_dfs_cac_status = DFS_CAC_NEVER_DONE; 532 hdd_son_deliver_cac_status_event(adapter, 533 ucfg_pre_cac_get_freq(vdev), 534 true); 535 } 536 } 537 538 static void 539 wlan_hdd_pre_cac_complete(struct wlan_objmgr_psoc *psoc, 540 uint8_t vdev_id, 541 QDF_STATUS status) 542 { 543 struct wlan_hdd_link_info *link_info; 544 545 link_info = wlan_hdd_get_link_info_from_vdev(psoc, vdev_id); 546 if (!link_info) { 547 hdd_err("Invalid vdev %d", vdev_id); 548 return; 549 } 550 551 if (QDF_IS_STATUS_SUCCESS(status)) 552 wlan_hdd_pre_cac_success(link_info->adapter); 553 else 554 wlan_hdd_pre_cac_failure(link_info->adapter); 555 } 556 557 struct osif_pre_cac_legacy_ops pre_cac_legacy_ops = { 558 .conditional_csa_ind_legacy_cb = 559 wlan_hdd_pre_cac_conditional_freq_switch_ind, 560 .pre_cac_complete_legacy_cb = wlan_hdd_pre_cac_complete, 561 }; 562 563 QDF_STATUS hdd_pre_cac_register_cb(void) 564 { 565 osif_pre_cac_set_legacy_cb(&pre_cac_legacy_ops); 566 567 return osif_pre_cac_register_cb(); 568 } 569 570 void hdd_pre_cac_unregister_cb(void) 571 { 572 osif_pre_cac_reset_legacy_cb(); 573 } 574