Qiwei Cai | e689a26 | 2018-07-26 15:50:22 +0800 | [diff] [blame^] | 1 | /* |
| 2 | * Copyright (c) 2012-2018 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: wlan_hdd_sap_cond_chan_switch.c |
| 21 | * |
| 22 | * WLAN SAP conditional channel switch functions |
| 23 | * |
| 24 | */ |
| 25 | |
| 26 | #include <wlan_hdd_includes.h> |
| 27 | #include <linux/netdevice.h> |
| 28 | #include <linux/skbuff.h> |
| 29 | #include <linux/etherdevice.h> |
| 30 | #include <linux/if_ether.h> |
| 31 | #include <cds_utils.h> |
| 32 | #include <qdf_str.h> |
| 33 | #include <wlan_hdd_sap_cond_chan_switch.h> |
| 34 | |
| 35 | /** |
| 36 | * wlan_hdd_set_pre_cac_status() - Set the pre cac status |
| 37 | * @pre_cac_adapter: AP adapter used for pre cac |
| 38 | * @status: Status (true or false) |
| 39 | * @handle: Global handle |
| 40 | * |
| 41 | * Sets the status of pre cac i.e., whether the pre cac is active or not |
| 42 | * |
| 43 | * Return: Zero on success, non-zero on failure |
| 44 | */ |
| 45 | static int wlan_hdd_set_pre_cac_status(struct hdd_adapter *pre_cac_adapter, |
| 46 | bool status, mac_handle_t handle) |
| 47 | { |
| 48 | QDF_STATUS ret; |
| 49 | |
| 50 | ret = wlan_sap_set_pre_cac_status( |
| 51 | WLAN_HDD_GET_SAP_CTX_PTR(pre_cac_adapter), status, handle); |
| 52 | if (QDF_IS_STATUS_ERROR(ret)) |
| 53 | return -EINVAL; |
| 54 | |
| 55 | return 0; |
| 56 | } |
| 57 | |
| 58 | /** |
| 59 | * wlan_hdd_set_chan_before_pre_cac() - Save the channel before pre cac |
| 60 | * @ap_adapter: AP adapter |
| 61 | * @chan_before_pre_cac: Channel |
| 62 | * |
| 63 | * Saves the channel which the AP was beaconing on before moving to the pre |
| 64 | * cac channel. If radar is detected on the pre cac channel, this saved |
| 65 | * channel will be used for AP operations. |
| 66 | * |
| 67 | * Return: Zero on success, non-zero on failure |
| 68 | */ |
| 69 | static int wlan_hdd_set_chan_before_pre_cac(struct hdd_adapter *ap_adapter, |
| 70 | uint8_t chan_before_pre_cac) |
| 71 | { |
| 72 | QDF_STATUS ret; |
| 73 | |
| 74 | ret = wlan_sap_set_chan_before_pre_cac( |
| 75 | WLAN_HDD_GET_SAP_CTX_PTR(ap_adapter), chan_before_pre_cac); |
| 76 | if (QDF_IS_STATUS_ERROR(ret)) |
| 77 | return -EINVAL; |
| 78 | |
| 79 | return 0; |
| 80 | } |
| 81 | |
| 82 | /** |
| 83 | * wlan_hdd_validate_and_get_pre_cac_ch() - Validate and get pre cac channel |
| 84 | * @hdd_ctx: HDD context |
| 85 | * @ap_adapter: AP adapter |
| 86 | * @channel: Channel requested by userspace |
| 87 | * @pre_cac_chan: Pointer to the pre CAC channel |
| 88 | * |
| 89 | * Validates the channel provided by userspace. If user provided channel 0, |
| 90 | * a valid outdoor channel must be selected from the regulatory channel. |
| 91 | * |
| 92 | * Return: Zero on success and non zero value on error |
| 93 | */ |
| 94 | static int wlan_hdd_validate_and_get_pre_cac_ch(struct hdd_context *hdd_ctx, |
| 95 | struct hdd_adapter *ap_adapter, |
| 96 | uint8_t channel, |
| 97 | uint8_t *pre_cac_chan) |
| 98 | { |
| 99 | uint32_t i; |
| 100 | QDF_STATUS status; |
| 101 | uint32_t weight_len = 0; |
| 102 | uint32_t len = WNI_CFG_VALID_CHANNEL_LIST_LEN; |
| 103 | uint8_t channel_list[QDF_MAX_NUM_CHAN] = {0}; |
| 104 | uint8_t pcl_weights[QDF_MAX_NUM_CHAN] = {0}; |
| 105 | mac_handle_t mac_handle; |
| 106 | |
| 107 | if (channel == 0) { |
| 108 | /* Channel is not obtained from PCL because PCL may not have |
| 109 | * the entire channel list. For example: if SAP is up on |
| 110 | * channel 6 and PCL is queried for the next SAP interface, |
| 111 | * if SCC is preferred, the PCL will contain only the channel |
| 112 | * 6. But, we are in need of a DFS channel. So, going with the |
| 113 | * first channel from the valid channel list. |
| 114 | */ |
| 115 | status = policy_mgr_get_valid_chans(hdd_ctx->hdd_psoc, |
| 116 | channel_list, &len); |
| 117 | if (QDF_IS_STATUS_ERROR(status)) { |
| 118 | hdd_err("Failed to get channel list"); |
| 119 | return -EINVAL; |
| 120 | } |
| 121 | policy_mgr_update_with_safe_channel_list(hdd_ctx->hdd_psoc, |
| 122 | channel_list, &len, |
| 123 | pcl_weights, |
| 124 | weight_len); |
| 125 | for (i = 0; i < len; i++) { |
| 126 | if (wlan_reg_is_dfs_ch(hdd_ctx->hdd_pdev, |
| 127 | channel_list[i])) { |
| 128 | *pre_cac_chan = channel_list[i]; |
| 129 | break; |
| 130 | } |
| 131 | } |
| 132 | if (*pre_cac_chan == 0) { |
| 133 | hdd_err("unable to find outdoor channel"); |
| 134 | return -EINVAL; |
| 135 | } |
| 136 | } else { |
| 137 | /* Only when driver selects a channel, check is done for |
| 138 | * unnsafe and NOL channels. When user provides a fixed channel |
| 139 | * the user is expected to take care of this. |
| 140 | */ |
| 141 | mac_handle = hdd_ctx->mac_handle; |
| 142 | if (!sme_is_channel_valid(mac_handle, channel) || |
| 143 | !wlan_reg_is_dfs_ch(hdd_ctx->hdd_pdev, channel)) { |
| 144 | hdd_err("Invalid channel for pre cac:%d", channel); |
| 145 | return -EINVAL; |
| 146 | } |
| 147 | *pre_cac_chan = channel; |
| 148 | } |
| 149 | hdd_debug("selected pre cac channel:%d", *pre_cac_chan); |
| 150 | return 0; |
| 151 | } |
| 152 | |
| 153 | /** |
| 154 | * wlan_hdd_request_pre_cac() - Start pre CAC in the driver |
| 155 | * @channel: Channel option provided by userspace |
| 156 | * |
| 157 | * Sets the driver to the required hardware mode and start an adapter for |
| 158 | * pre CAC which will mimic an AP. |
| 159 | * |
| 160 | * Return: Zero on success, non-zero value on error |
| 161 | */ |
| 162 | int wlan_hdd_request_pre_cac(uint8_t channel) |
| 163 | { |
| 164 | uint8_t pre_cac_chan = 0, *mac_addr; |
| 165 | struct hdd_context *hdd_ctx; |
| 166 | int ret; |
| 167 | struct hdd_adapter *ap_adapter, *pre_cac_adapter; |
| 168 | struct hdd_ap_ctx *hdd_ap_ctx; |
| 169 | QDF_STATUS status; |
| 170 | struct wiphy *wiphy; |
| 171 | struct net_device *dev; |
| 172 | struct cfg80211_chan_def chandef; |
| 173 | enum nl80211_channel_type channel_type; |
| 174 | uint32_t freq; |
| 175 | struct ieee80211_channel *chan; |
| 176 | mac_handle_t mac_handle; |
| 177 | bool val; |
| 178 | |
| 179 | hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); |
| 180 | if (wlan_hdd_validate_context(hdd_ctx) != 0) |
| 181 | return -EINVAL; |
| 182 | |
| 183 | if (policy_mgr_get_connection_count(hdd_ctx->hdd_psoc) > 1) { |
| 184 | hdd_err("pre cac not allowed in concurrency"); |
| 185 | return -EINVAL; |
| 186 | } |
| 187 | |
| 188 | ap_adapter = hdd_get_adapter(hdd_ctx, QDF_SAP_MODE); |
| 189 | if (!ap_adapter) { |
| 190 | hdd_err("unable to get SAP adapter"); |
| 191 | return -EINVAL; |
| 192 | } |
| 193 | |
| 194 | mac_handle = hdd_ctx->mac_handle; |
| 195 | val = wlan_sap_is_pre_cac_active(mac_handle); |
| 196 | if (val) { |
| 197 | hdd_err("pre cac is already in progress"); |
| 198 | return -EINVAL; |
| 199 | } |
| 200 | |
| 201 | hdd_ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(ap_adapter); |
| 202 | if (!hdd_ap_ctx) { |
| 203 | hdd_err("SAP context is NULL"); |
| 204 | return -EINVAL; |
| 205 | } |
| 206 | |
| 207 | if (wlan_reg_is_dfs_ch(hdd_ctx->hdd_pdev, |
| 208 | hdd_ap_ctx->operating_channel)) { |
| 209 | hdd_err("SAP is already on DFS channel:%d", |
| 210 | hdd_ap_ctx->operating_channel); |
| 211 | return -EINVAL; |
| 212 | } |
| 213 | |
| 214 | if (!WLAN_REG_IS_24GHZ_CH(hdd_ap_ctx->operating_channel)) { |
| 215 | hdd_err("pre CAC alllowed only when SAP is in 2.4GHz:%d", |
| 216 | hdd_ap_ctx->operating_channel); |
| 217 | return -EINVAL; |
| 218 | } |
| 219 | |
| 220 | mac_addr = wlan_hdd_get_intf_addr(hdd_ctx); |
| 221 | if (!mac_addr) { |
| 222 | hdd_err("can't add virtual intf: Not getting valid mac addr"); |
| 223 | return -EINVAL; |
| 224 | } |
| 225 | |
| 226 | hdd_debug("channel:%d", channel); |
| 227 | |
| 228 | ret = wlan_hdd_validate_and_get_pre_cac_ch(hdd_ctx, ap_adapter, channel, |
| 229 | &pre_cac_chan); |
| 230 | if (ret != 0) { |
| 231 | hdd_err("can't validate pre-cac channel"); |
| 232 | goto release_intf_addr_and_return_failure; |
| 233 | } |
| 234 | |
| 235 | hdd_debug("starting pre cac SAP adapter"); |
| 236 | |
| 237 | /* Starting a SAP adapter: |
| 238 | * Instead of opening an adapter, we could just do a SME open session |
| 239 | * for AP type. But, start BSS would still need an adapter. |
| 240 | * So, this option is not taken. |
| 241 | * |
| 242 | * hdd open adapter is going to register this precac interface with |
| 243 | * user space. This interface though exposed to user space will be in |
| 244 | * DOWN state. Consideration was done to avoid this registration to the |
| 245 | * user space. But, as part of SAP operations multiple events are sent |
| 246 | * to user space. Some of these events received from unregistered |
| 247 | * interface was causing crashes. So, retaining the registration. |
| 248 | * |
| 249 | * So, this interface would remain registered and will remain in DOWN |
| 250 | * state for the CAC duration. We will add notes in the feature |
| 251 | * announcement to not use this temporary interface for any activity |
| 252 | * from user space. |
| 253 | */ |
| 254 | pre_cac_adapter = hdd_open_adapter(hdd_ctx, QDF_SAP_MODE, "precac%d", |
| 255 | mac_addr, NET_NAME_UNKNOWN, true); |
| 256 | if (!pre_cac_adapter) { |
| 257 | hdd_err("error opening the pre cac adapter"); |
| 258 | goto release_intf_addr_and_return_failure; |
| 259 | } |
| 260 | |
| 261 | /* |
| 262 | * This interface is internally created by the driver. So, no interface |
| 263 | * up comes for this interface from user space and hence starting |
| 264 | * the adapter internally. |
| 265 | */ |
| 266 | if (hdd_start_adapter(pre_cac_adapter)) { |
| 267 | hdd_err("error starting the pre cac adapter"); |
| 268 | goto close_pre_cac_adapter; |
| 269 | } |
| 270 | |
| 271 | hdd_debug("preparing for start ap/bss on the pre cac adapter"); |
| 272 | |
| 273 | wiphy = hdd_ctx->wiphy; |
| 274 | dev = pre_cac_adapter->dev; |
| 275 | |
| 276 | /* Since this is only a dummy interface lets us use the IEs from the |
| 277 | * other active SAP interface. In regular scenarios, these IEs would |
| 278 | * come from the user space entity |
| 279 | */ |
| 280 | pre_cac_adapter->session.ap.beacon = qdf_mem_malloc( |
| 281 | sizeof(*ap_adapter->session.ap.beacon)); |
| 282 | if (!pre_cac_adapter->session.ap.beacon) { |
| 283 | hdd_err("failed to alloc mem for beacon"); |
| 284 | goto stop_close_pre_cac_adapter; |
| 285 | } |
| 286 | qdf_mem_copy(pre_cac_adapter->session.ap.beacon, |
| 287 | ap_adapter->session.ap.beacon, |
| 288 | sizeof(*pre_cac_adapter->session.ap.beacon)); |
| 289 | pre_cac_adapter->session.ap.sap_config.ch_width_orig = |
| 290 | ap_adapter->session.ap.sap_config.ch_width_orig; |
| 291 | pre_cac_adapter->session.ap.sap_config.authType = |
| 292 | ap_adapter->session.ap.sap_config.authType; |
| 293 | |
| 294 | /* Premise is that on moving from 2.4GHz to 5GHz, the SAP will continue |
| 295 | * to operate on the same bandwidth as that of the 2.4GHz operations. |
| 296 | * Only bandwidths 20MHz/40MHz are possible on 2.4GHz band. |
| 297 | */ |
| 298 | switch (ap_adapter->session.ap.sap_config.ch_width_orig) { |
| 299 | case CH_WIDTH_20MHZ: |
| 300 | channel_type = NL80211_CHAN_HT20; |
| 301 | break; |
| 302 | case CH_WIDTH_40MHZ: |
| 303 | if (ap_adapter->session.ap.sap_config.sec_ch > |
| 304 | ap_adapter->session.ap.sap_config.channel) |
| 305 | channel_type = NL80211_CHAN_HT40PLUS; |
| 306 | else |
| 307 | channel_type = NL80211_CHAN_HT40MINUS; |
| 308 | break; |
| 309 | default: |
| 310 | channel_type = NL80211_CHAN_NO_HT; |
| 311 | break; |
| 312 | } |
| 313 | |
| 314 | freq = cds_chan_to_freq(pre_cac_chan); |
| 315 | chan = ieee80211_get_channel(wiphy, freq); |
| 316 | if (!chan) { |
| 317 | hdd_err("channel converion failed"); |
| 318 | goto stop_close_pre_cac_adapter; |
| 319 | } |
| 320 | |
| 321 | cfg80211_chandef_create(&chandef, chan, channel_type); |
| 322 | |
| 323 | hdd_debug("orig width:%d channel_type:%d freq:%d", |
| 324 | ap_adapter->session.ap.sap_config.ch_width_orig, |
| 325 | channel_type, freq); |
| 326 | /* |
| 327 | * Doing update after opening and starting pre-cac adapter will make |
| 328 | * sure that driver won't do hardware mode change if there are any |
| 329 | * initial hick-ups or issues in pre-cac adapter's configuration. |
| 330 | * Since current SAP is in 2.4GHz and pre CAC channel is in 5GHz, this |
| 331 | * connection update should result in DBS mode |
| 332 | */ |
| 333 | status = policy_mgr_update_and_wait_for_connection_update( |
| 334 | hdd_ctx->hdd_psoc, |
| 335 | ap_adapter->session_id, |
| 336 | pre_cac_chan, |
| 337 | POLICY_MGR_UPDATE_REASON_PRE_CAC); |
| 338 | if (QDF_IS_STATUS_ERROR(status)) { |
| 339 | hdd_err("error in moving to DBS mode"); |
| 340 | goto stop_close_pre_cac_adapter; |
| 341 | } |
| 342 | |
| 343 | ret = wlan_hdd_set_channel(wiphy, dev, &chandef, channel_type); |
| 344 | if (ret != 0) { |
| 345 | hdd_err("failed to set channel"); |
| 346 | goto stop_close_pre_cac_adapter; |
| 347 | } |
| 348 | |
| 349 | status = wlan_hdd_cfg80211_start_bss(pre_cac_adapter, NULL, |
| 350 | PRE_CAC_SSID, qdf_str_len(PRE_CAC_SSID), |
| 351 | NL80211_HIDDEN_SSID_NOT_IN_USE, false); |
| 352 | if (QDF_IS_STATUS_ERROR(status)) { |
| 353 | hdd_err("start bss failed"); |
| 354 | goto stop_close_pre_cac_adapter; |
| 355 | } |
| 356 | |
| 357 | /* |
| 358 | * The pre cac status is set here. But, it would not be reset explicitly |
| 359 | * anywhere, since after the pre cac success/failure, the pre cac |
| 360 | * adapter itself would be removed. |
| 361 | */ |
| 362 | ret = wlan_hdd_set_pre_cac_status(pre_cac_adapter, true, mac_handle); |
| 363 | if (ret != 0) { |
| 364 | hdd_err("failed to set pre cac status"); |
| 365 | goto stop_close_pre_cac_adapter; |
| 366 | } |
| 367 | |
| 368 | ret = wlan_hdd_set_chan_before_pre_cac(ap_adapter, |
| 369 | hdd_ap_ctx->operating_channel); |
| 370 | if (ret != 0) { |
| 371 | hdd_err("failed to set channel before pre cac"); |
| 372 | goto stop_close_pre_cac_adapter; |
| 373 | } |
| 374 | |
| 375 | ap_adapter->pre_cac_chan = pre_cac_chan; |
| 376 | |
| 377 | return 0; |
| 378 | |
| 379 | stop_close_pre_cac_adapter: |
| 380 | hdd_stop_adapter(hdd_ctx, pre_cac_adapter); |
| 381 | qdf_mem_free(pre_cac_adapter->session.ap.beacon); |
| 382 | pre_cac_adapter->session.ap.beacon = NULL; |
| 383 | close_pre_cac_adapter: |
| 384 | hdd_close_adapter(hdd_ctx, pre_cac_adapter, false); |
| 385 | release_intf_addr_and_return_failure: |
| 386 | /* |
| 387 | * Release the interface address as the adapter |
| 388 | * failed to start, if you don't release then next |
| 389 | * adapter which is trying to come wouldn't get valid |
| 390 | * mac address. Remember we have limited pool of mac addresses |
| 391 | */ |
| 392 | wlan_hdd_release_intf_addr(hdd_ctx, mac_addr); |
| 393 | return -EINVAL; |
| 394 | } |
| 395 | |
| 396 | /** |
| 397 | * __wlan_hdd_cfg80211_conditional_chan_switch() - Conditional channel switch |
| 398 | * @wiphy: Pointer to wireless phy |
| 399 | * @wdev: Pointer to wireless device |
| 400 | * @data: Pointer to data |
| 401 | * @data_len: Data length |
| 402 | * |
| 403 | * Processes the conditional channel switch request and invokes the helper |
| 404 | * APIs to process the channel switch request. |
| 405 | * |
| 406 | * Return: 0 on success, negative errno on failure |
| 407 | */ |
| 408 | static int |
| 409 | __wlan_hdd_cfg80211_conditional_chan_switch(struct wiphy *wiphy, |
| 410 | struct wireless_dev *wdev, |
| 411 | const void *data, |
| 412 | int data_len) |
| 413 | { |
| 414 | int ret; |
| 415 | struct hdd_context *hdd_ctx = wiphy_priv(wiphy); |
| 416 | struct net_device *dev = wdev->netdev; |
| 417 | struct hdd_adapter *adapter; |
| 418 | struct nlattr |
| 419 | *tb[QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_MAX + 1]; |
| 420 | uint32_t freq_len, i; |
| 421 | uint32_t *freq; |
| 422 | uint8_t chans[QDF_MAX_NUM_CHAN] = {0}; |
| 423 | |
| 424 | hdd_enter_dev(dev); |
| 425 | |
| 426 | ret = wlan_hdd_validate_context(hdd_ctx); |
| 427 | if (ret) |
| 428 | return ret; |
| 429 | |
| 430 | if (!hdd_ctx->config->enableDFSMasterCap) { |
| 431 | hdd_err("DFS master capability is not present in the driver"); |
| 432 | return -EINVAL; |
| 433 | } |
| 434 | |
| 435 | if (hdd_get_conparam() == QDF_GLOBAL_FTM_MODE) { |
| 436 | hdd_err("Command not allowed in FTM mode"); |
| 437 | return -EPERM; |
| 438 | } |
| 439 | |
| 440 | adapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| 441 | if (adapter->device_mode != QDF_SAP_MODE) { |
| 442 | hdd_err("Invalid device mode %d", adapter->device_mode); |
| 443 | return -EINVAL; |
| 444 | } |
| 445 | |
| 446 | /* |
| 447 | * audit note: it is ok to pass a NULL policy here since only |
| 448 | * one attribute is parsed which is array of frequencies and |
| 449 | * it is explicitly validated for both under read and over read |
| 450 | */ |
| 451 | if (wlan_cfg80211_nla_parse(tb, |
| 452 | QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_MAX, |
| 453 | data, data_len, NULL)) { |
| 454 | hdd_err("Invalid ATTR"); |
| 455 | return -EINVAL; |
| 456 | } |
| 457 | |
| 458 | if (!tb[QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_FREQ_LIST]) { |
| 459 | hdd_err("Frequency list is missing"); |
| 460 | return -EINVAL; |
| 461 | } |
| 462 | |
| 463 | freq_len = nla_len( |
| 464 | tb[QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_FREQ_LIST])/ |
| 465 | sizeof(uint32_t); |
| 466 | |
| 467 | if (freq_len > QDF_MAX_NUM_CHAN) { |
| 468 | hdd_err("insufficient space to hold channels"); |
| 469 | return -ENOMEM; |
| 470 | } |
| 471 | |
| 472 | hdd_debug("freq_len=%d", freq_len); |
| 473 | |
| 474 | freq = nla_data( |
| 475 | tb[QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_FREQ_LIST]); |
| 476 | |
| 477 | for (i = 0; i < freq_len; i++) { |
| 478 | if (freq[i] == 0) |
| 479 | chans[i] = 0; |
| 480 | else |
| 481 | chans[i] = ieee80211_frequency_to_channel(freq[i]); |
| 482 | |
| 483 | hdd_debug("freq[%d]=%d", i, freq[i]); |
| 484 | } |
| 485 | |
| 486 | /* |
| 487 | * The input frequency list from user space is designed to be a |
| 488 | * priority based frequency list. This is only to accommodate any |
| 489 | * future request. But, current requirement is only to perform CAC |
| 490 | * on a single channel. So, the first entry from the list is picked. |
| 491 | * |
| 492 | * If channel is zero, any channel in the available outdoor regulatory |
| 493 | * domain will be selected. |
| 494 | */ |
| 495 | ret = wlan_hdd_request_pre_cac(chans[0]); |
| 496 | if (ret) { |
| 497 | hdd_err("pre cac request failed with reason:%d", ret); |
| 498 | return ret; |
| 499 | } |
| 500 | |
| 501 | return 0; |
| 502 | } |
| 503 | |
| 504 | int wlan_hdd_cfg80211_conditional_chan_switch(struct wiphy *wiphy, |
| 505 | struct wireless_dev *wdev, |
| 506 | const void *data, |
| 507 | int data_len) |
| 508 | { |
| 509 | int ret; |
| 510 | |
| 511 | cds_ssr_protect(__func__); |
| 512 | ret = __wlan_hdd_cfg80211_conditional_chan_switch(wiphy, wdev, |
| 513 | data, data_len); |
| 514 | cds_ssr_unprotect(__func__); |
| 515 | |
| 516 | return ret; |
| 517 | } |
| 518 | |