/* * Copyright (c) 2012-2021, The Linux Foundation. All rights reserved. * Copyright (c) 2021-2024, Qualcomm Innovation Center, Inc. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * DOC: hdd_cm_connect.c * * WLAN Host Device Driver connect APIs implementation * */ #include "wlan_hdd_main.h" #include #include "wlan_hdd_cm_api.h" #include "wlan_hdd_trace.h" #include "wlan_hdd_object_manager.h" #include "wlan_hdd_power.h" #include "wlan_hdd_connectivity_logging.h" #include #include #include #include #include #include #include #include #include #include "wlan_crypto_global_api.h" #include "wlan_vdev_mgr_ucfg_api.h" #include "wlan_hdd_bootup_marker.h" #include "sme_qos_internal.h" #include "wlan_dlm_ucfg_api.h" #include "wlan_hdd_scan.h" #include "wlan_osif_priv.h" #include #include #include "wlan_roam_debug.h" #include #include "wlan_hdd_hostapd.h" #include #include #include "wlan_osif_features.h" #include "wlan_osif_request_manager.h" #include #include "wlan_psoc_mlme_ucfg_api.h" #include "wlan_action_oui_ucfg_api.h" bool hdd_cm_is_vdev_associated(struct wlan_hdd_link_info *link_info) { struct wlan_objmgr_vdev *vdev; bool is_vdev_active; enum QDF_OPMODE opmode; struct hdd_station_ctx *sta_ctx; sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(link_info); if (link_info->adapter->device_mode == QDF_NDI_MODE) return (sta_ctx->conn_info.conn_state == eConnectionState_NdiConnected); vdev = hdd_objmgr_get_vdev_by_user(link_info, WLAN_OSIF_CM_ID); if (!vdev) return false; opmode = wlan_vdev_mlme_get_opmode(vdev); if (opmode != QDF_STA_MODE && opmode != QDF_P2P_CLIENT_MODE) { hdd_objmgr_put_vdev_by_user(vdev, WLAN_OSIF_CM_ID); return false; } is_vdev_active = ucfg_cm_is_vdev_active(vdev); hdd_objmgr_put_vdev_by_user(vdev, WLAN_OSIF_CM_ID); return is_vdev_active; } bool hdd_cm_is_vdev_connected(struct wlan_hdd_link_info *link_info) { struct wlan_objmgr_vdev *vdev; bool is_vdev_connected; struct hdd_station_ctx *sta_ctx; enum QDF_OPMODE opmode = link_info->adapter->device_mode; sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(link_info); if (opmode == QDF_NDI_MODE) return (sta_ctx->conn_info.conn_state == eConnectionState_NdiConnected); if (opmode != QDF_STA_MODE && opmode != QDF_P2P_CLIENT_MODE) return false; vdev = hdd_objmgr_get_vdev_by_user(link_info, WLAN_OSIF_CM_ID); if (!vdev) return false; is_vdev_connected = ucfg_cm_is_vdev_connected(vdev); hdd_objmgr_put_vdev_by_user(vdev, WLAN_OSIF_CM_ID); return is_vdev_connected; } bool hdd_cm_is_connecting(struct wlan_hdd_link_info *link_info) { struct wlan_objmgr_vdev *vdev; bool is_vdev_connecting; enum QDF_OPMODE opmode; vdev = hdd_objmgr_get_vdev_by_user(link_info, WLAN_OSIF_CM_ID); if (!vdev) return false; opmode = wlan_vdev_mlme_get_opmode(vdev); if (opmode != QDF_STA_MODE && opmode != QDF_P2P_CLIENT_MODE) { hdd_objmgr_put_vdev_by_user(vdev, WLAN_OSIF_CM_ID); return false; } is_vdev_connecting = ucfg_cm_is_vdev_connecting(vdev); hdd_objmgr_put_vdev_by_user(vdev, WLAN_OSIF_CM_ID); return is_vdev_connecting; } bool hdd_cm_is_disconnected(struct wlan_hdd_link_info *link_info) { struct wlan_objmgr_vdev *vdev; bool is_vdev_disconnected; enum QDF_OPMODE opmode; vdev = hdd_objmgr_get_vdev_by_user(link_info, WLAN_OSIF_CM_ID); if (!vdev) return false; opmode = wlan_vdev_mlme_get_opmode(vdev); if (opmode != QDF_STA_MODE && opmode != QDF_P2P_CLIENT_MODE) { hdd_objmgr_put_vdev_by_user(vdev, WLAN_OSIF_CM_ID); return false; } is_vdev_disconnected = ucfg_cm_is_vdev_disconnected(vdev); hdd_objmgr_put_vdev_by_user(vdev, WLAN_OSIF_CM_ID); return is_vdev_disconnected; } bool hdd_cm_is_vdev_roaming(struct wlan_hdd_link_info *link_info) { struct wlan_objmgr_vdev *vdev; bool is_vdev_roaming; enum QDF_OPMODE opmode; vdev = hdd_objmgr_get_vdev_by_user(link_info, WLAN_OSIF_CM_ID); if (!vdev) return false; opmode = wlan_vdev_mlme_get_opmode(vdev); if (opmode != QDF_STA_MODE && opmode != QDF_P2P_CLIENT_MODE) { hdd_objmgr_put_vdev_by_user(vdev, WLAN_OSIF_CM_ID); return false; } is_vdev_roaming = ucfg_cm_is_vdev_roaming(vdev); hdd_objmgr_put_vdev_by_user(vdev, WLAN_OSIF_CM_ID); return is_vdev_roaming; } void hdd_cm_set_peer_authenticate(struct wlan_hdd_link_info *link_info, struct qdf_mac_addr *bssid, bool is_auth_required) { hdd_debug("sta: " QDF_MAC_ADDR_FMT "Changing TL state to %s", QDF_MAC_ADDR_REF(bssid->bytes), is_auth_required ? "CONNECTED" : "AUTHENTICATED"); hdd_change_peer_state(link_info, bssid->bytes, is_auth_required ? OL_TXRX_PEER_STATE_CONN : OL_TXRX_PEER_STATE_AUTH); hdd_conn_set_authenticated(link_info, !is_auth_required); hdd_objmgr_set_peer_mlme_auth_state(link_info->vdev, !is_auth_required); } void hdd_cm_update_rssi_snr_by_bssid(struct wlan_hdd_link_info *link_info) { struct hdd_station_ctx *sta_ctx; int8_t snr = 0; struct hdd_adapter *adapter = link_info->adapter; mac_handle_t mac_handle; if (!adapter) { hdd_err_rl("null hdd_adapter pointer"); return; } mac_handle = hdd_adapter_get_mac_handle(adapter); if (!mac_handle) { hdd_err_rl("null mac_handle pointer"); return; } sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(link_info); hdd_get_rssi_snr_by_bssid(mac_handle, sta_ctx->conn_info.bssid.bytes, &link_info->rssi, &snr); /* If RSSi is reported as positive then it is invalid */ if (link_info->rssi > 0) { hdd_debug_rl("RSSI invalid %d", link_info->rssi); link_info->rssi = 0; } hdd_debug("snr: %d, rssi: %d", snr, link_info->rssi); sta_ctx->conn_info.signal = link_info->rssi; sta_ctx->conn_info.noise = sta_ctx->conn_info.signal - snr; sta_ctx->cache_conn_info.signal = sta_ctx->conn_info.signal; sta_ctx->cache_conn_info.noise = sta_ctx->conn_info.noise; } void hdd_cm_handle_assoc_event(struct wlan_objmgr_vdev *vdev, uint8_t *peer_mac) { struct hdd_context *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); struct wlan_hdd_link_info *link_info; struct hdd_station_ctx *sta_ctx; int ret; if (!hdd_ctx) { hdd_err("hdd_ctx is NULL"); return; } link_info = hdd_get_link_info_by_vdev(hdd_ctx, wlan_vdev_get_id(vdev)); if (!link_info) { hdd_err("adapter is NULL for vdev %d", wlan_vdev_get_id(vdev)); return; } sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(link_info); ret = hdd_objmgr_set_peer_mlme_state(link_info->vdev, WLAN_ASSOC_STATE); if (ret) hdd_err("Peer object " QDF_MAC_ADDR_FMT " fail to set associated state", QDF_MAC_ADDR_REF(peer_mac)); ucfg_dp_add_latency_critical_client(vdev, hdd_convert_cfgdot11mode_to_80211mode( sta_ctx->conn_info.dot11mode)); ucfg_dp_bus_bw_compute_prev_txrx_stats(vdev); ucfg_dp_bus_bw_compute_timer_start(hdd_ctx->psoc); if (ucfg_pkt_capture_get_pktcap_mode(hdd_ctx->psoc)) ucfg_pkt_capture_record_channel(link_info->vdev); } /** * hdd_cm_netif_features_update_required() - Check if feature update * is required * @adapter: pointer to the adapter structure * Returns: true if the connection is legacy and TSO and Checksum offload * enabled or if the connection is not latency and TSO and Checksum * offload are not enabled, false otherwise */ static bool hdd_cm_netif_features_update_required(struct hdd_adapter *adapter) { bool is_legacy_connection = hdd_is_legacy_connection(adapter->deflink); hdd_debug("Legacy Connection: %d, TSO_CSUM Feature Enabled:%d", is_legacy_connection, adapter->tso_csum_feature_enabled); if (adapter->tso_csum_feature_enabled && is_legacy_connection) return true; if (!adapter->tso_csum_feature_enabled && !is_legacy_connection) return true; return false; } void hdd_cm_netif_queue_enable(struct hdd_adapter *adapter) { ol_txrx_soc_handle soc = cds_get_context(QDF_MODULE_ID_SOC); struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); if (cdp_cfg_get(soc, cfg_dp_disable_legacy_mode_csum_offload) && hdd_cm_netif_features_update_required(adapter)) { hdd_adapter_ops_record_event(hdd_ctx, WLAN_HDD_ADAPTER_OPS_WORK_POST, adapter->deflink->vdev_id); qdf_queue_work(0, hdd_ctx->adapter_ops_wq, &adapter->netdev_features_update_work); } wlan_hdd_netif_queue_control(adapter, WLAN_WAKE_ALL_NETIF_QUEUE, WLAN_CONTROL_PATH); } void hdd_cm_clear_pmf_stats(struct hdd_adapter *adapter) { qdf_mem_zero(&adapter->deflink->hdd_stats.hdd_pmf_stats, sizeof(adapter->deflink->hdd_stats.hdd_pmf_stats)); } void hdd_cm_save_connect_status(struct wlan_hdd_link_info *link_info, uint32_t reason_code) { struct hdd_station_ctx *hdd_sta_ctx; hdd_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(link_info); link_info->adapter->connect_req_status = reason_code; hdd_sta_ctx->conn_info.assoc_status_code = reason_code; hdd_sta_ctx->cache_conn_info.assoc_status_code = reason_code; } #ifdef WLAN_FEATURE_11BE_MLO QDF_STATUS hdd_cm_save_connected_links_info(struct qdf_mac_addr *self_mac, struct qdf_mac_addr *bssid, int32_t link_id) { struct hdd_context *hdd_ctx; struct wlan_hdd_link_info *link_info; struct hdd_station_ctx *sta_ctx; hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); if (!hdd_ctx) { hdd_err("HDD context NULL"); return QDF_STATUS_E_INVAL; } link_info = hdd_get_link_info_by_link_addr(hdd_ctx, self_mac); if (!link_info) { hdd_err("No link info with MAC: " QDF_MAC_ADDR_FMT, QDF_MAC_ADDR_REF(self_mac->bytes)); return QDF_STATUS_E_INVAL; } sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(link_info); hdd_cm_set_ieee_link_id(link_info, link_id); qdf_copy_macaddr(&sta_ctx->conn_info.bssid, bssid); return QDF_STATUS_SUCCESS; } void hdd_cm_set_ieee_link_id(struct wlan_hdd_link_info *link_info, uint8_t link_id) { struct hdd_station_ctx *sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(link_info); hdd_debug("old_link_id:%d new_link_id:%d", sta_ctx->conn_info.ieee_link_id, link_id); sta_ctx->conn_info.ieee_link_id = link_id; } void hdd_cm_clear_ieee_link_id(struct wlan_hdd_link_info *link_info) { struct hdd_station_ctx *sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(link_info); hdd_debug("clear link id:%d", sta_ctx->conn_info.ieee_link_id); sta_ctx->conn_info.ieee_link_id = WLAN_INVALID_LINK_ID; } #endif #ifdef FEATURE_WLAN_WAPI static bool hdd_cm_is_wapi_sta(enum csr_akm_type auth_type) { if (auth_type == eCSR_AUTH_TYPE_WAPI_WAI_CERTIFICATE || auth_type == eCSR_AUTH_TYPE_WAPI_WAI_PSK) return true; else return false; } #else static inline bool hdd_cm_is_wapi_sta(enum csr_akm_type auth_type) { return false; } #endif static void hdd_update_scan_ie_for_connect(struct hdd_adapter *adapter, struct osif_connect_params *params) { if (adapter->device_mode == QDF_P2P_CLIENT_MODE) { params->scan_ie.ptr = &adapter->scan_info.scan_add_ie.addIEdata[0]; params->scan_ie.len = adapter->scan_info.scan_add_ie.length; } else if (adapter->scan_info.default_scan_ies) { params->scan_ie.ptr = adapter->scan_info.default_scan_ies; params->scan_ie.len = adapter->scan_info.default_scan_ies_len; } else if (adapter->scan_info.scan_add_ie.length) { params->scan_ie.ptr = adapter->scan_info.scan_add_ie.addIEdata; params->scan_ie.len = adapter->scan_info.scan_add_ie.length; } } #if ((LINUX_VERSION_CODE >= KERNEL_VERSION(5, 19, 0)) || \ defined(CFG80211_11BE_BASIC)) && \ defined(WLAN_FEATURE_11BE) /** * hdd_update_action_oui_for_connect() - Update Action OUI for 802.11be AP * @hdd_ctx: hdd context * @req: connect request parameter * * If user sets flag ASSOC_REQ_DISABLE_EHT in connect request, driver * will send action oui "ffffff 00 01" to host mlme and also firmware * for action id ACTION_OUI_11BE_OUI_ALLOW, so that all the AP will * be not matched with this OUI and 802.11be mode will not be allowed, * possibly downgrade to 11ax will happen. * If user doesn't set ASSOC_REQ_DISABLE_EHT, driver/firmware will * recover to default INI setting. * * Returns: void */ static void hdd_update_action_oui_for_connect(struct hdd_context *hdd_ctx, struct cfg80211_connect_params *req) { QDF_STATUS status; uint8_t *str; bool usr_disable_eht; if (!ucfg_action_oui_enabled(hdd_ctx->psoc)) return; usr_disable_eht = ucfg_mlme_get_usr_disable_sta_eht(hdd_ctx->psoc); if (req->flags & ASSOC_REQ_DISABLE_EHT || !(req->flags & CONNECT_REQ_MLO_SUPPORT)) { if (usr_disable_eht) { hdd_debug("user eht is disabled already"); return; } status = ucfg_action_oui_cleanup( hdd_ctx->psoc, ACTION_OUI_11BE_OUI_ALLOW); if (!QDF_IS_STATUS_SUCCESS(status)) { hdd_err("Failed to cleanup oui id %d", ACTION_OUI_11BE_OUI_ALLOW); return; } status = ucfg_action_oui_parse(hdd_ctx->psoc, ACTION_OUI_INVALID, ACTION_OUI_11BE_OUI_ALLOW); if (!QDF_IS_STATUS_SUCCESS(status)) { hdd_err("Failed to parse action_oui str for id %d", ACTION_OUI_11BE_OUI_ALLOW); return; } } else { if (!usr_disable_eht) { hdd_debug("user eht is enabled already"); return; } status = ucfg_action_oui_cleanup(hdd_ctx->psoc, ACTION_OUI_11BE_OUI_ALLOW); if (!QDF_IS_STATUS_SUCCESS(status)) { hdd_err("Failed to cleanup oui id %d", ACTION_OUI_11BE_OUI_ALLOW); return; } str = ucfg_action_oui_get_config(hdd_ctx->psoc, ACTION_OUI_11BE_OUI_ALLOW); if (!qdf_str_len(str)) goto send_oui; status = ucfg_action_oui_parse(hdd_ctx->psoc, str, ACTION_OUI_11BE_OUI_ALLOW); if (!QDF_IS_STATUS_SUCCESS(status)) { hdd_err("Failed to parse action_oui str for id %d", ACTION_OUI_11BE_OUI_ALLOW); return; } } send_oui: status = ucfg_action_oui_send_by_id(hdd_ctx->psoc, ACTION_OUI_11BE_OUI_ALLOW); if (!QDF_IS_STATUS_SUCCESS(status)) { hdd_err("Failed to send oui id %d", ACTION_OUI_11BE_OUI_ALLOW); return; } ucfg_mlme_set_usr_disable_sta_eht(hdd_ctx->psoc, !usr_disable_eht); } #else static void hdd_update_action_oui_for_connect(struct hdd_context *hdd_ctx, struct cfg80211_connect_params *req) { } #endif #ifdef WLAN_FEATURE_11BE static inline bool hdd_config_is_dot11mode_11be_only(struct hdd_config *config) { if (config->dot11Mode == eHDD_DOT11_MODE_11be_ONLY) return true; else return false; } #else static inline bool hdd_config_is_dot11mode_11be_only(struct hdd_config *config) { return false; } #endif /** * hdd_get_dot11mode_filter() - Get dot11 mode filter * @hdd_ctx: HDD context * * This function is used to get the dot11 mode filter * * Context: Any Context. * Return: dot11_mode_filter */ static enum dot11_mode_filter hdd_get_dot11mode_filter(struct hdd_context *hdd_ctx) { struct hdd_config *config = hdd_ctx->config; if (config->dot11Mode == eHDD_DOT11_MODE_11n_ONLY) return ALLOW_11N_ONLY; else if (config->dot11Mode == eHDD_DOT11_MODE_11ac_ONLY) return ALLOW_11AC_ONLY; else if (config->dot11Mode == eHDD_DOT11_MODE_11ax_ONLY) return ALLOW_11AX_ONLY; else if (hdd_config_is_dot11mode_11be_only(config)) return ALLOW_11BE_ONLY; else return ALLOW_ALL; } /** * hdd_get_sap_adapter_of_dfs - Get sap adapter on dfs channel * @hdd_ctx: HDD context * * This function is used to get the sap adapter on dfs channel. * * Return: pointer to adapter or null */ static struct hdd_adapter * hdd_get_sap_adapter_of_dfs(struct hdd_context *hdd_ctx) { struct hdd_adapter *adapter, *next_adapter = NULL; wlan_net_dev_ref_dbgid dbgid = NET_DEV_HOLD_GET_ADAPTER; struct wlan_channel *chan; struct ch_params ch_params = {0}; struct wlan_hdd_link_info *link_info; hdd_for_each_adapter_dev_held_safe(hdd_ctx, adapter, next_adapter, dbgid) { if (adapter->device_mode != QDF_SAP_MODE) goto loop_next; hdd_adapter_for_each_active_link_info(adapter, link_info) { if (wlan_hdd_validate_vdev_id(link_info->vdev_id)) continue; /* * sap is not in started state and also not under doing * CAC, so it is fine to go ahead with sta. */ if (!test_bit(SOFTAP_BSS_STARTED, &link_info->link_flags) && hdd_ctx->dev_dfs_cac_status != DFS_CAC_IN_PROGRESS) continue; wlan_objmgr_vdev_get_ref(link_info->vdev, WLAN_HDD_ID_OBJ_MGR); chan = wlan_vdev_get_active_channel(link_info->vdev); if (!chan) { hdd_debug("Can not get active channel"); wlan_objmgr_vdev_release_ref(link_info->vdev, WLAN_HDD_ID_OBJ_MGR); continue; } wlan_objmgr_vdev_release_ref(link_info->vdev, WLAN_HDD_ID_OBJ_MGR); if (!wlan_reg_is_5ghz_ch_freq(chan->ch_freq)) continue; ch_params.ch_width = chan->ch_width; if (ch_params.ch_width == CH_WIDTH_160MHZ) wlan_reg_set_create_punc_bitmap(&ch_params, true); if (wlan_reg_get_5g_bonded_channel_state_for_pwrmode( hdd_ctx->pdev, chan->ch_freq, &ch_params, REG_CURRENT_PWR_MODE) == CHANNEL_STATE_DFS) { hdd_adapter_dev_put_debug(adapter, dbgid); if (next_adapter) hdd_adapter_dev_put_debug(next_adapter, dbgid); return adapter; } } loop_next: hdd_adapter_dev_put_debug(adapter, dbgid); } return NULL; } /** * wlan_hdd_cm_handle_sap_sta_dfs_conc() - to handle SAP STA DFS conc * @hdd_ctx: hdd_ctx * @req: connect req * * This routine will move SAP from dfs to non-dfs, if sta is coming up. * * Return: false if sta-sap conc is not allowed, else return true */ static bool wlan_hdd_cm_handle_sap_sta_dfs_conc(struct hdd_context *hdd_ctx, struct cfg80211_connect_params *req) { struct hdd_adapter *ap_adapter; struct hdd_ap_ctx *hdd_ap_ctx; struct hdd_hostapd_state *hostapd_state; QDF_STATUS status; qdf_freq_t ch_freq = 0; enum phy_ch_width ch_bw; enum channel_state ch_state; struct scan_filter *scan_filter; qdf_list_t *list = NULL; qdf_list_node_t *cur_lst = NULL; struct scan_cache_node *cur_node = NULL; bool is_6ghz_cap = false; ap_adapter = hdd_get_sap_adapter_of_dfs(hdd_ctx); /* probably no dfs sap running, no handling required */ if (!ap_adapter) return true; /* if sap is currently doing CAC then don't allow sta to go further */ if (hdd_ctx->dev_dfs_cac_status == DFS_CAC_IN_PROGRESS) { hdd_err("Concurrent SAP is in CAC state, STA is not allowed"); return false; } /* * log and return error, if we allow STA to go through, we don't * know what is going to happen better stop sta connection */ hdd_ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(ap_adapter->deflink); if (!hdd_ap_ctx) { hdd_err("AP context not found"); return false; } if (req->channel && req->channel->center_freq) { ch_freq = req->channel->center_freq; goto def_chan; } /* * find out by looking in to scan cache where sta is going to * connect by passing its ssid amd bssid. */ scan_filter = qdf_mem_malloc(sizeof(*scan_filter)); if (!scan_filter) goto def_chan; if (req->bssid) { scan_filter->num_of_bssid = 1; qdf_mem_copy(scan_filter->bssid_list[0].bytes, req->bssid, QDF_MAC_ADDR_SIZE); } if (req->ssid_len > WLAN_SSID_MAX_LEN) { scan_filter->num_of_ssid = 0; hdd_err("req ssid len invalid %zu", req->ssid_len); } else { scan_filter->num_of_ssid = 1; scan_filter->ssid_list[0].length = req->ssid_len; qdf_mem_copy(scan_filter->ssid_list[0].ssid, req->ssid, scan_filter->ssid_list[0].length); } scan_filter->ignore_auth_enc_type = true; list = ucfg_scan_get_result(hdd_ctx->pdev, scan_filter); qdf_mem_free(scan_filter); if (!list || (list && !qdf_list_size(list))) { hdd_debug("scan list empty"); goto purge_list; } qdf_list_peek_front(list, &cur_lst); if (!cur_lst) goto purge_list; cur_node = qdf_container_of(cur_lst, struct scan_cache_node, node); ch_freq = cur_node->entry->channel.chan_freq; purge_list: if (list) ucfg_scan_purge_results(list); def_chan: /* * If the STA's channel is 2.4 GHz, then set pcl with only 2.4 GHz * channels for roaming case. */ if (WLAN_REG_IS_24GHZ_CH_FREQ(ch_freq)) { hdd_info("sap is on dfs, new sta conn on 2.4 is allowed"); return true; } if (policy_mgr_is_hw_sbs_capable(hdd_ctx->psoc) && ch_freq && policy_mgr_are_sbs_chan(hdd_ctx->psoc, ch_freq, hdd_ap_ctx->operating_chan_freq)) { hdd_debug("sta freq %d sap freq %d in sbs mode is allowed", ch_freq, hdd_ap_ctx->operating_chan_freq); return true; } /* * If channel is 0 or DFS or LTE unsafe then better to call pcl and * find out the best channel. If channel is non-dfs 5 GHz then * better move SAP to STA's channel to make scc, so we have room * for 3port MCC scenario. */ ch_bw = hdd_ap_ctx->sap_config.ch_width_orig; if (ch_freq) is_6ghz_cap = policy_mgr_get_ap_6ghz_capable(hdd_ctx->psoc, ap_adapter->deflink->vdev_id, NULL); if (!ch_freq || wlan_reg_is_dfs_for_freq(hdd_ctx->pdev, ch_freq) || !policy_mgr_is_safe_channel(hdd_ctx->psoc, ch_freq) || wlan_reg_is_passive_for_freq(hdd_ctx->pdev, ch_freq) || (WLAN_REG_IS_6GHZ_CHAN_FREQ(ch_freq) && !is_6ghz_cap)) ch_freq = policy_mgr_get_nondfs_preferred_channel( hdd_ctx->psoc, PM_SAP_MODE, true, ap_adapter->deflink->vdev_id); if (WLAN_REG_IS_5GHZ_CH_FREQ(ch_freq) && ch_bw > CH_WIDTH_20MHZ) { struct ch_params ch_params; qdf_mem_zero(&ch_params, sizeof(ch_params)); ch_params.ch_width = ch_bw; ch_state = wlan_reg_get_5g_bonded_channel_state_for_pwrmode( hdd_ctx->pdev, ch_freq, &ch_params, REG_CURRENT_PWR_MODE); while (ch_bw > CH_WIDTH_20MHZ && ch_state != CHANNEL_STATE_ENABLE) { ch_bw = wlan_reg_get_next_lower_bandwidth(ch_bw); ch_params.ch_width = ch_bw; ch_state = wlan_reg_get_5g_bonded_channel_state_for_pwrmode (hdd_ctx->pdev, ch_freq, &ch_params, REG_CURRENT_PWR_MODE); } hdd_debug("bw change from %d to %d", hdd_ap_ctx->sap_config.ch_width_orig, ch_bw); } hostapd_state = WLAN_HDD_GET_HOSTAP_STATE_PTR(ap_adapter->deflink); qdf_event_reset(&hostapd_state->qdf_event); wlan_hdd_set_sap_csa_reason(hdd_ctx->psoc, ap_adapter->deflink->vdev_id, CSA_REASON_STA_CONNECT_DFS_TO_NON_DFS); status = wlansap_set_channel_change_with_csa( WLAN_HDD_GET_SAP_CTX_PTR(ap_adapter->deflink), ch_freq, ch_bw, false); if (QDF_STATUS_SUCCESS != status) { hdd_err("Set channel with CSA IE failed, can't allow STA"); return false; } /* * wait here for SAP to finish the channel switch. When channel * switch happens, SAP sends few beacons with CSA_IE. After * successfully Transmission of those beacons, it will move its * state from started to disconnected and move to new channel. * once it moves to new channel, sap again moves its state * machine from disconnected to started and set this event. * wait for 10 secs to finish this. */ status = qdf_wait_for_event_completion(&hostapd_state->qdf_event, 10000); if (!QDF_IS_STATUS_SUCCESS(status)) { hdd_err("wait for qdf_event failed, STA not allowed!!"); return false; } return true; } int wlan_hdd_cm_connect(struct wiphy *wiphy, struct net_device *ndev, struct cfg80211_connect_params *req) { int status; struct wlan_objmgr_vdev *vdev; struct osif_connect_params params; struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(ndev); struct hdd_context *hdd_ctx; struct hdd_station_ctx *hdd_sta_ctx; qdf_freq_t ch_freq = 0; hdd_enter(); if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { hdd_err("Command not allowed in FTM mode"); return -EINVAL; } if (wlan_hdd_validate_vdev_id(adapter->deflink->vdev_id)) return -EINVAL; qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, TRACE_CODE_HDD_CFG80211_CONNECT, adapter->deflink->vdev_id, adapter->device_mode); if (adapter->device_mode != QDF_STA_MODE && adapter->device_mode != QDF_P2P_CLIENT_MODE) { hdd_err("Device_mode %s(%d) is not supported", qdf_opmode_str(adapter->device_mode), adapter->device_mode); return -EINVAL; } hdd_ctx = WLAN_HDD_GET_CTX(adapter); hdd_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter->deflink); status = wlan_hdd_validate_context(hdd_ctx); if (status) return status; hdd_reg_wait_for_country_change(hdd_ctx); if (policy_mgr_is_hw_dbs_capable(hdd_ctx->psoc) && !wlan_hdd_cm_handle_sap_sta_dfs_conc(hdd_ctx, req)) { hdd_err("sap-sta conc will fail, can't allow sta"); return -EINVAL; } if (req->channel && req->channel->center_freq) ch_freq = req->channel->center_freq; if (ch_freq && wlan_reg_is_6ghz_chan_freq(ch_freq) && !wlan_reg_is_6ghz_band_set(hdd_ctx->pdev)) { hdd_err("6 GHz band disabled"); return -EINVAL; } qdf_mem_zero(¶ms, sizeof(params)); ucfg_dlm_dump_deny_list_ap(hdd_ctx->pdev); vdev = hdd_objmgr_get_vdev_by_user(adapter->deflink, WLAN_OSIF_CM_ID); if (!vdev) return -EINVAL; ucfg_pmo_flush_gtk_offload_req(vdev); qdf_mem_zero(&hdd_sta_ctx->conn_info.conn_flag, sizeof(hdd_sta_ctx->conn_info.conn_flag)); /* * Reset the ptk, gtk status flags to avoid using old/previous * connection status. */ hdd_sta_ctx->conn_info.gtk_installed = false; hdd_sta_ctx->conn_info.ptk_installed = false; adapter->last_disconnect_reason = 0; qdf_runtime_pm_prevent_suspend(&hdd_ctx->runtime_context.connect); hdd_prevent_suspend_timeout(HDD_WAKELOCK_CONNECT_COMPLETE, WIFI_POWER_EVENT_WAKELOCK_CONNECT); params.force_rsne_override = hdd_ctx->force_rsne_override; params.dot11mode_filter = hdd_get_dot11mode_filter(hdd_ctx); hdd_update_scan_ie_for_connect(adapter, ¶ms); hdd_update_action_oui_for_connect(hdd_ctx, req); if (!hdd_cm_is_vdev_associated(adapter->deflink)) { /* * Clear user/wpa_supplicant disabled_roaming flag for new * connection */ ucfg_clear_user_disabled_roaming(hdd_ctx->psoc, adapter->deflink->vdev_id); } status = osif_cm_connect(ndev, vdev, req, ¶ms); if (status || ucfg_cm_is_vdev_roaming(vdev)) { /* Release suspend and wake lock for failure or roam invoke */ if (status) hdd_err("Vdev %d connect failed status %d", adapter->deflink->vdev_id, status); else hdd_debug("Vdev %d: connect lead to roam invoke", adapter->deflink->vdev_id); qdf_runtime_pm_allow_suspend(&hdd_ctx->runtime_context.connect); hdd_allow_suspend(WIFI_POWER_EVENT_WAKELOCK_CONNECT); } hdd_objmgr_put_vdev_by_user(vdev, WLAN_OSIF_CM_ID); return status; } static void hdd_cm_rec_connect_info(struct wlan_cm_connect_resp *rsp) { if (rsp->is_reassoc) wlan_rec_conn_info(rsp->vdev_id, DEBUG_CONN_ROAMED_IND, rsp->bssid.bytes, rsp->cm_id, 0); else wlan_rec_conn_info(rsp->vdev_id, DEBUG_CONN_CONNECT_RESULT, rsp->bssid.bytes, rsp->cm_id << 16 | rsp->reason, rsp->status_code); } static void hdd_cm_connect_failure_pre_user_update(struct wlan_objmgr_vdev *vdev, struct wlan_cm_connect_resp *rsp) { struct hdd_context *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); struct hdd_adapter *adapter; struct hdd_station_ctx *hdd_sta_ctx; uint32_t time_buffer_size; struct wlan_hdd_link_info *link_info; bool is_link_switch = wlan_vdev_mlme_is_mlo_link_switch_in_progress(vdev); if (!hdd_ctx) { hdd_err("hdd_ctx is NULL"); return; } link_info = hdd_get_link_info_by_vdev(hdd_ctx, rsp->vdev_id); if (!link_info) { hdd_err("adapter is NULL vdev %d", rsp->vdev_id); return; } adapter = link_info->adapter; hdd_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(link_info); time_buffer_size = sizeof(hdd_sta_ctx->conn_info.connect_time); qdf_mem_zero(hdd_sta_ctx->conn_info.connect_time, time_buffer_size); hdd_init_scan_reject_params(hdd_ctx); hdd_cm_save_connect_status(link_info, rsp->status_code); if (!is_link_switch) { /* For link switch connection failure, do not clear existing * connection info in OSIF. */ hdd_conn_remove_connect_info(hdd_sta_ctx); hdd_adapter_reset_station_ctx(adapter); } ucfg_dp_remove_conn_info(vdev); hdd_cm_update_rssi_snr_by_bssid(link_info); hdd_cm_rec_connect_info(rsp); hdd_debug("Invoking packetdump deregistration API"); wlan_deregister_txrx_packetdump(OL_TXRX_PDEV_ID); } static void hdd_cm_connect_failure_post_user_update(struct wlan_objmgr_vdev *vdev, struct wlan_cm_connect_resp *rsp) { struct hdd_context *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); struct hdd_adapter *adapter; struct wlan_hdd_link_info *link_info; bool is_roam = rsp->is_reassoc; if (!hdd_ctx) { hdd_err("hdd_ctx is NULL"); return; } link_info = hdd_get_link_info_by_vdev(hdd_ctx, rsp->vdev_id); if (!link_info) { hdd_err("adapter is NULL for vdev %d", rsp->vdev_id); return; } if (!is_roam) { /* call only for connect */ qdf_runtime_pm_allow_suspend(&hdd_ctx->runtime_context.connect); hdd_allow_suspend(WIFI_POWER_EVENT_WAKELOCK_CONNECT); } adapter = link_info->adapter; wlan_hdd_connectivity_fail_event(vdev, rsp); hdd_clear_roam_profile_ie(adapter); ucfg_cm_reset_key(hdd_ctx->pdev, link_info->vdev_id); hdd_wmm_dscp_initial_state(adapter); /* * If connect failure is due to link switch, do not disable the * netdev queues as it will lead to data stall/NUD failure. */ if (!(rsp->cm_id & CM_ID_LSWITCH_BIT)) { hdd_debug("Disabling queues"); wlan_hdd_netif_queue_control(adapter, WLAN_STOP_ALL_NETIF_QUEUE_N_CARRIER, WLAN_CONTROL_PATH); } ucfg_dp_periodic_sta_stats_start(vdev); wlan_twt_concurrency_update(hdd_ctx); } static void hdd_cm_connect_failure(struct wlan_objmgr_vdev *vdev, struct wlan_cm_connect_resp *rsp, enum osif_cb_type type) { switch (type) { case OSIF_PRE_USERSPACE_UPDATE: hdd_cm_connect_failure_pre_user_update(vdev, rsp); break; case OSIF_POST_USERSPACE_UPDATE: hdd_cm_connect_failure_post_user_update(vdev, rsp); break; default: hdd_cm_connect_failure_pre_user_update(vdev, rsp); hdd_cm_connect_failure_post_user_update(vdev, rsp); } } /** * hdd_cm_update_prev_ap_ie() - Update the connected AP IEs * @hdd_sta_ctx: Station context. * @rsp: Connect response * * This API updates the connected ap beacon IEs to station context connection * info. * * Return: None */ static void hdd_cm_update_prev_ap_ie(struct hdd_station_ctx *hdd_sta_ctx, struct wlan_cm_connect_resp *rsp) { struct element_info *bcn_probe_rsp = &rsp->connect_ies.bcn_probe_rsp; struct element_info *bcn_ie; uint32_t len; bcn_ie = &hdd_sta_ctx->conn_info.prev_ap_bcn_ie; if (bcn_ie->ptr) { qdf_mem_free(bcn_ie->ptr); bcn_ie->ptr = NULL; bcn_ie->len = 0; } if (bcn_probe_rsp->ptr && bcn_probe_rsp->len > sizeof(struct wlan_frame_hdr)) { len = bcn_probe_rsp->len - sizeof(struct wlan_frame_hdr); bcn_ie->ptr = qdf_mem_malloc(len); if (!bcn_ie->ptr) { bcn_ie->len = 0; return; } qdf_mem_copy(bcn_ie->ptr, bcn_probe_rsp->ptr + sizeof(struct wlan_frame_hdr), len); bcn_ie->len = len; } } static void hdd_cm_save_bss_info(struct wlan_hdd_link_info *link_info, struct wlan_cm_connect_resp *rsp) { struct hdd_context *hdd_ctx; struct hdd_station_ctx *hdd_sta_ctx; QDF_STATUS status; struct hdd_adapter *adapter = link_info->adapter; mac_handle_t mac_handle = hdd_adapter_get_mac_handle(adapter); struct sDot11fAssocResponse *assoc_resp; if (!mac_handle) { hdd_err("mac handle is null"); return; } hdd_ctx = WLAN_HDD_GET_CTX(adapter); hdd_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(link_info); assoc_resp = qdf_mem_malloc(sizeof(struct sDot11fAssocResponse)); if (!assoc_resp) return; qdf_mem_zero(&hdd_sta_ctx->conn_info.hs20vendor_ie, sizeof(hdd_sta_ctx->conn_info.hs20vendor_ie)); if (rsp->connect_ies.bcn_probe_rsp.ptr) sme_get_hs20vendor_ie(mac_handle, rsp->connect_ies.bcn_probe_rsp.ptr, rsp->connect_ies.bcn_probe_rsp.len, &hdd_sta_ctx->conn_info.hs20vendor_ie); status = sme_unpack_assoc_rsp(mac_handle, rsp, assoc_resp); if (QDF_IS_STATUS_ERROR(status)) { hdd_err("could not parse assoc response"); qdf_mem_free(assoc_resp); return; } if (assoc_resp->VHTCaps.present) { hdd_sta_ctx->conn_info.conn_flag.vht_present = true; hdd_copy_vht_caps(&hdd_sta_ctx->conn_info.vht_caps, &assoc_resp->VHTCaps); } else { hdd_sta_ctx->conn_info.conn_flag.vht_present = false; } if (assoc_resp->HTCaps.present) { hdd_sta_ctx->conn_info.conn_flag.ht_present = true; hdd_copy_ht_caps(&hdd_sta_ctx->conn_info.ht_caps, &assoc_resp->HTCaps); } else { hdd_sta_ctx->conn_info.conn_flag.ht_present = false; } if (rsp->is_reassoc) hdd_sta_ctx->conn_info.roam_count++; if (assoc_resp->HTInfo.present) { hdd_sta_ctx->conn_info.conn_flag.ht_op_present = true; hdd_copy_ht_operation(hdd_sta_ctx, &assoc_resp->HTInfo); } else { hdd_sta_ctx->conn_info.conn_flag.ht_op_present = false; } if (assoc_resp->VHTOperation.present) { hdd_sta_ctx->conn_info.conn_flag.vht_op_present = true; hdd_copy_vht_operation(hdd_sta_ctx, &assoc_resp->VHTOperation); } else { hdd_sta_ctx->conn_info.conn_flag.vht_op_present = false; } if (assoc_resp->he_cap.present) hdd_sta_ctx->conn_info.conn_flag.he_present = true; else hdd_sta_ctx->conn_info.conn_flag.he_present = false; if (assoc_resp->eht_cap.present) hdd_sta_ctx->conn_info.conn_flag.eht_present = true; else hdd_sta_ctx->conn_info.conn_flag.eht_present = false; if (assoc_resp->eht_op.present) hdd_sta_ctx->conn_info.conn_flag.eht_op_present = true; else hdd_sta_ctx->conn_info.conn_flag.eht_op_present = false; /* * Cache connection info only in case of station */ if (adapter->device_mode == QDF_STA_MODE) { /* Cleanup already existing he info */ hdd_cleanup_conn_info(link_info); hdd_copy_eht_operation(hdd_sta_ctx, &assoc_resp->eht_op); /* Cache last connection info */ qdf_mem_copy(&hdd_sta_ctx->cache_conn_info, &hdd_sta_ctx->conn_info, sizeof(hdd_sta_ctx->cache_conn_info)); hdd_copy_he_operation(hdd_sta_ctx, &assoc_resp->he_op); hdd_cm_update_prev_ap_ie(hdd_sta_ctx, rsp); } qdf_mem_free(assoc_resp); } #ifdef FEATURE_WLAN_ESE static bool hdd_is_ese_assoc(enum csr_akm_type auth_type, tDot11fBeaconIEs *bcn_ie, struct mac_context *mac_ctx) { if ((csr_is_auth_type_ese(auth_type) || (bcn_ie->ESEVersion.present && auth_type == eCSR_AUTH_TYPE_OPEN_SYSTEM)) && mac_ctx->mlme_cfg->lfr.ese_enabled) { return true; } return false; } #else static bool hdd_is_ese_assoc(enum csr_akm_type auth_type, tDot11fBeaconIEs *bcn_ie, struct mac_context *mac_ctx) { return false; } #endif static const uint8_t acm_mask_bit[WLAN_MAX_AC] = { 0x4, /* SME_AC_BK */ 0x8, /* SME_AC_BE */ 0x2, /* SME_AC_VI */ 0x1 /* SME_AC_VO */ }; static void hdd_wmm_cm_connect(struct wlan_objmgr_vdev *vdev, struct hdd_adapter *adapter, tDot11fBeaconIEs *bcn_ie, enum csr_akm_type auth_type) { int ac; bool qap; bool qos_connection; uint8_t acm_mask = 0; struct vdev_mlme_obj *vdev_mlme; mac_handle_t mac_handle = hdd_adapter_get_mac_handle(adapter); struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle); if (!mac_handle) { hdd_err("mac handle is null"); return; } vdev_mlme = wlan_vdev_mlme_get_cmpt_obj(vdev); if (!vdev_mlme) { hdd_err("vdev component object is NULL"); return; } if (CSR_IS_QOS_BSS(bcn_ie) || bcn_ie->HTCaps.present) /* Some HT AP's dont send WMM IE so in that case we * assume all HT Ap's are Qos Enabled AP's */ qap = true; else qap = false; qos_connection = vdev_mlme->ext_vdev_ptr->connect_info.qos_enabled; acm_mask = sme_qos_get_acm_mask(mac_ctx, NULL, bcn_ie); hdd_debug("qap is %d, qos_connection is %d, acm_mask is 0x%x", qap, qos_connection, acm_mask); adapter->hdd_wmm_status.qap = qap; adapter->hdd_wmm_status.qos_connection = qos_connection; if (acm_mask) QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_PE, QDF_TRACE_LEVEL_DEBUG, (uint8_t *)acm_mask_bit, WLAN_MAX_AC); for (ac = 0; ac < WLAN_MAX_AC; ac++) { if (qap && qos_connection && (acm_mask & acm_mask_bit[ac])) { /* admission is required */ adapter->hdd_wmm_status.ac_status[ac]. is_access_required = true; adapter->hdd_wmm_status.ac_status[ac]. is_access_allowed = false; adapter->hdd_wmm_status.ac_status[ac]. was_access_granted = false; /* after reassoc if we have valid tspec, allow access */ if (adapter->hdd_wmm_status.ac_status[ac]. is_tspec_valid && (adapter->hdd_wmm_status.ac_status[ac]. tspec.ts_info.direction != SME_QOS_WMM_TS_DIR_DOWNLINK)) { adapter->hdd_wmm_status.ac_status[ac]. is_access_allowed = true; } if (!sme_neighbor_roam_is11r_assoc( mac_handle, adapter->deflink->vdev_id) && !hdd_is_ese_assoc(auth_type, bcn_ie, mac_ctx)) { adapter->hdd_wmm_status.ac_status[ac]. is_tspec_valid = false; adapter->hdd_wmm_status.ac_status[ac]. is_access_allowed = false; } } else { /* admission is not required so access is allowed */ adapter->hdd_wmm_status.ac_status[ac]. is_access_required = false; adapter->hdd_wmm_status.ac_status[ac]. is_access_allowed = true; } } } static void hdd_cm_save_connect_info(struct wlan_hdd_link_info *link_info, struct wlan_cm_connect_resp *rsp) { struct hdd_station_ctx *sta_ctx; struct wlan_crypto_params *crypto_params; struct wlan_channel *des_chan; struct wlan_objmgr_vdev *vdev; uint8_t *ie_field; uint32_t ie_len, status; tDot11fBeaconIEs *bcn_ie; struct s_ext_cap *p_ext_cap = NULL; struct hdd_adapter *adapter = link_info->adapter; mac_handle_t mac_handle = hdd_adapter_get_mac_handle(adapter); uint32_t phymode; sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(link_info); bcn_ie = qdf_mem_malloc(sizeof(*bcn_ie)); if (!bcn_ie) return; qdf_copy_macaddr(&sta_ctx->conn_info.bssid, &rsp->bssid); vdev = hdd_objmgr_get_vdev_by_user(link_info, WLAN_DP_ID); if (vdev) { ucfg_dp_conn_info_set_bssid(vdev, &rsp->bssid); hdd_objmgr_put_vdev_by_user(vdev, WLAN_DP_ID); } phymode = wlan_reg_get_max_phymode(adapter->hdd_ctx->pdev, REG_PHYMODE_MAX, rsp->freq); sta_ctx->reg_phymode = csr_convert_from_reg_phy_mode(phymode); sta_ctx->conn_info.assoc_status_code = rsp->status_code; crypto_params = wlan_crypto_vdev_get_crypto_params(link_info->vdev); if (crypto_params) { sme_fill_enc_type(&sta_ctx->conn_info.uc_encrypt_type, crypto_params->ucastcipherset); sme_fill_auth_type(&sta_ctx->conn_info.auth_type, crypto_params->authmodeset, crypto_params->key_mgmt, crypto_params->ucastcipherset); sta_ctx->conn_info.last_auth_type = sta_ctx->conn_info.auth_type; } des_chan = wlan_vdev_mlme_get_des_chan(link_info->vdev); sta_ctx->conn_info.chan_freq = rsp->freq; /* Save the ssid for the connection */ qdf_mem_copy(&sta_ctx->conn_info.ssid.SSID.ssId, &rsp->ssid.ssid, rsp->ssid.length); qdf_mem_copy(&sta_ctx->conn_info.last_ssid.SSID.ssId, &rsp->ssid.ssid, rsp->ssid.length); sta_ctx->conn_info.ssid.SSID.length = rsp->ssid.length; sta_ctx->conn_info.last_ssid.SSID.length = rsp->ssid.length; sta_ctx->conn_info.dot11mode = sme_phy_mode_to_dot11mode(des_chan->ch_phymode); sta_ctx->conn_info.ch_width = des_chan->ch_width; if (!rsp->connect_ies.bcn_probe_rsp.ptr || (rsp->connect_ies.bcn_probe_rsp.len < (sizeof(struct wlan_frame_hdr) + offsetof(struct wlan_bcn_frame, ie)))) { hdd_err("beacon len is invalid %d", rsp->connect_ies.bcn_probe_rsp.len); qdf_mem_free(bcn_ie); return; } ie_len = (rsp->connect_ies.bcn_probe_rsp.len - sizeof(struct wlan_frame_hdr) - offsetof(struct wlan_bcn_frame, ie)); ie_field = (uint8_t *)(rsp->connect_ies.bcn_probe_rsp.ptr + sizeof(struct wlan_frame_hdr) + offsetof(struct wlan_bcn_frame, ie)); status = dot11f_unpack_beacon_i_es(MAC_CONTEXT(mac_handle), ie_field, ie_len, bcn_ie, false); if (DOT11F_FAILED(status)) { hdd_err("Failed to parse beacon ie"); qdf_mem_free(bcn_ie); return; } if (bcn_ie->ExtCap.present) { p_ext_cap = (struct s_ext_cap *)bcn_ie->ExtCap.bytes; sta_ctx->conn_info.proxy_arp_service = p_ext_cap->proxy_arp_service; } vdev = hdd_objmgr_get_vdev_by_user(link_info, WLAN_OSIF_CM_ID); if (vdev) { sta_ctx->conn_info.nss = wlan_vdev_mlme_get_nss(vdev); ucfg_wlan_vdev_mgr_get_param(vdev, WLAN_MLME_CFG_RATE_FLAGS, &sta_ctx->conn_info.rate_flags); hdd_wmm_cm_connect(vdev, adapter, bcn_ie, sta_ctx->conn_info.auth_type); if (p_ext_cap) ucfg_dp_conn_info_set_arp_service(vdev, p_ext_cap->proxy_arp_service); hdd_objmgr_put_vdev_by_user(vdev, WLAN_OSIF_CM_ID); } qdf_mem_free(bcn_ie); hdd_cm_save_bss_info(link_info, rsp); } #ifdef WLAN_FEATURE_FILS_SK static bool hdd_cm_is_fils_connection(struct wlan_cm_connect_resp *rsp) { return rsp->is_fils_connection; } #else static inline bool hdd_cm_is_fils_connection(struct wlan_cm_connect_resp *rsp) { return false; } #endif #ifdef WLAN_FEATURE_ROAM_OFFLOAD static bool hdd_cm_is_roam_auth_required(struct hdd_station_ctx *sta_ctx, struct wlan_cm_connect_resp *rsp) { if (!rsp->roaming_info) return false; if (rsp->roaming_info->auth_status == ROAM_AUTH_STATUS_AUTHENTICATED) return false; return true; } #else static bool hdd_cm_is_roam_auth_required(struct hdd_station_ctx *sta_ctx, struct wlan_cm_connect_resp *rsp) { return true; } #endif #if defined(WLAN_FEATURE_11BE_MLO) && defined(CFG80211_11BE_BASIC) #ifndef WLAN_HDD_MULTI_VDEV_SINGLE_NDEV struct hdd_adapter *hdd_get_assoc_link_adapter(struct hdd_adapter *ml_adapter) { int i; bool eht_capab; struct hdd_adapter *link_adapter; struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(ml_adapter); ucfg_psoc_mlme_get_11be_capab(hdd_ctx->psoc, &eht_capab); if (!eht_capab || hdd_adapter_is_sl_ml_adapter(ml_adapter)) return ml_adapter; for (i = 0; i < WLAN_MAX_MLD; i++) { link_adapter = ml_adapter->mlo_adapter_info.link_adapter[i]; if (link_adapter) { if (hdd_adapter_is_associated_with_ml_adapter( link_adapter)) return link_adapter; } } return NULL; } #endif #ifdef WLAN_HDD_MULTI_VDEV_SINGLE_NDEV static void hdd_set_immediate_power_save(struct hdd_adapter *adapter, bool is_immediate_powersave) { struct hdd_station_ctx *sta_ctx; struct wlan_hdd_link_info *link_info; hdd_adapter_for_each_active_link_info(adapter, link_info) { sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(link_info); sta_ctx->ap_supports_immediate_power_save = is_immediate_powersave; } } #else static void hdd_set_immediate_power_save(struct hdd_adapter *adapter, bool is_immediate_powersave) { struct hdd_adapter *link_adapter; struct hdd_mlo_adapter_info *mlo_adapter_info; struct hdd_station_ctx *sta_ctx; int i; if (!hdd_adapter_is_ml_adapter(adapter)) goto update_non_ml; mlo_adapter_info = &adapter->mlo_adapter_info; for (i = 0; i < WLAN_MAX_MLD; i++) { link_adapter = mlo_adapter_info->link_adapter[i]; if (!link_adapter) continue; sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(link_adapter->deflink); sta_ctx->ap_supports_immediate_power_save = is_immediate_powersave; } update_non_ml: sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter->deflink); sta_ctx->ap_supports_immediate_power_save = is_immediate_powersave; } #endif #else static void hdd_set_immediate_power_save(struct hdd_adapter *adapter, bool is_immediate_powersave) { struct hdd_station_ctx *sta_ctx; sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter->deflink); sta_ctx->ap_supports_immediate_power_save = is_immediate_powersave; } #endif #if defined(WLAN_FEATURE_11BE_MLO) && defined(WLAN_HDD_MULTI_VDEV_SINGLE_NDEV) static QDF_STATUS hdd_cm_mlme_send_standby_link_chn_width(struct hdd_adapter *adapter, struct wlan_objmgr_vdev *vdev) { struct wlan_objmgr_psoc *psoc; struct wlan_hdd_link_info *link_info; struct hdd_station_ctx *sta_ctx; uint8_t link_id = wlan_vdev_get_link_id(vdev); uint8_t ch_width; enum phy_ch_width connection_ch_width = CH_WIDTH_INVALID; psoc = wlan_vdev_get_psoc(vdev); if (!psoc) { hdd_err("Failed to get PSOC Object"); return QDF_STATUS_E_INVAL; } link_info = hdd_get_link_info_by_ieee_link_id(adapter, link_id); if (!link_info) { hdd_err("Link info not found by linkid:%u", link_id); return QDF_STATUS_E_INVAL; } sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(link_info); ch_width = sta_ctx->user_cfg_chn_width; wlan_mlme_get_sta_ch_width(vdev, &connection_ch_width); if (ch_width == CH_WIDTH_INVALID) { hdd_debug("no cached bandwidth for the link %u", link_id); return QDF_STATUS_SUCCESS; } if (ch_width == connection_ch_width) { hdd_debug("user config max bd same as connection ch bw:%u", ch_width); return QDF_STATUS_SUCCESS; } hdd_debug("send vdev id:%u, chwidth:%u", link_info->vdev_id, ch_width); wlan_mlme_send_ch_width_update_with_notify(psoc, vdev, link_info->vdev_id, ch_width); return QDF_STATUS_SUCCESS; } #else static QDF_STATUS hdd_cm_mlme_send_standby_link_chn_width(struct hdd_adapter *adapter, struct wlan_objmgr_vdev *vdev) { return QDF_STATUS_SUCCESS; } #endif static void hdd_cm_connect_success_pre_user_update(struct wlan_objmgr_vdev *vdev, struct wlan_cm_connect_resp *rsp) { struct hdd_context *hdd_ctx; struct hdd_adapter *adapter; struct hdd_station_ctx *sta_ctx; struct vdev_mlme_obj *vdev_mlme; unsigned long rc; uint32_t ie_len; uint8_t *ie_field; mac_handle_t mac_handle; bool is_auth_required = true; bool is_roam_offload = false; bool is_roam = rsp->is_reassoc; ol_txrx_soc_handle soc = cds_get_context(QDF_MODULE_ID_SOC); uint8_t uapsd_mask = 0; uint32_t time_buffer_size; struct hdd_adapter *assoc_link_adapter; bool is_immediate_power_save; struct wlan_hdd_link_info *link_info; QDF_STATUS status = QDF_STATUS_E_INVAL; bool alt_pipe; hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); if (!hdd_ctx) { hdd_err("hdd_ctx is NULL"); return; } link_info = hdd_get_link_info_by_vdev(hdd_ctx, wlan_vdev_get_id(vdev)); if (!link_info) { hdd_err("Invalid vdev"); return; } adapter = link_info->adapter; sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(link_info); mac_handle = hdd_adapter_get_mac_handle(adapter); wlan_hdd_ft_set_key_delay(vdev); hdd_cm_update_rssi_snr_by_bssid(link_info); hdd_cm_save_connect_status(link_info, rsp->status_code); hdd_init_scan_reject_params(hdd_ctx); time_buffer_size = sizeof(sta_ctx->conn_info.connect_time); qdf_mem_zero(sta_ctx->conn_info.connect_time, time_buffer_size); qdf_get_time_of_the_day_in_hr_min_sec_usec(sta_ctx->conn_info.connect_time, time_buffer_size); hdd_start_tsf_sync(adapter); hdd_cm_rec_connect_info(rsp); hdd_cm_save_connect_info(link_info, rsp); if (adapter->device_mode == QDF_STA_MODE && hdd_adapter_is_ml_adapter(adapter)) { /* Save connection info in assoc link adapter as well */ assoc_link_adapter = hdd_get_assoc_link_adapter(adapter); if (assoc_link_adapter) hdd_cm_save_connect_info(assoc_link_adapter->deflink, rsp); } if (hdd_add_beacon_filter(adapter) != 0) hdd_err("add beacon filter failed"); adapter->wapi_info.is_wapi_sta = hdd_cm_is_wapi_sta( sta_ctx->conn_info.auth_type); if (adapter->device_mode == QDF_STA_MODE && rsp->connect_ies.bcn_probe_rsp.ptr && (rsp->connect_ies.bcn_probe_rsp.len > (sizeof(struct wlan_frame_hdr) + offsetof(struct wlan_bcn_frame, ie)))) { ie_len = (rsp->connect_ies.bcn_probe_rsp.len - sizeof(struct wlan_frame_hdr) - offsetof(struct wlan_bcn_frame, ie)); ie_field = (uint8_t *)(rsp->connect_ies.bcn_probe_rsp.ptr + sizeof(struct wlan_frame_hdr) + offsetof(struct wlan_bcn_frame, ie)); is_immediate_power_save = wlan_hdd_is_ap_supports_immediate_power_save( ie_field, ie_len); hdd_set_immediate_power_save(adapter, is_immediate_power_save); hdd_debug("ap_supports_immediate_power_save flag [%d]", sta_ctx->ap_supports_immediate_power_save); } hdd_green_ap_start_state_mc(hdd_ctx, adapter->device_mode, true); hdd_cm_handle_assoc_event(vdev, rsp->bssid.bytes); if (ucfg_cm_is_link_switch_connect_resp(rsp)) { if (hdd_cm_mlme_send_standby_link_chn_width(adapter, vdev)) hdd_debug("send standby link chn width fail"); } /* * check update hdd_send_update_beacon_ies_event, * hdd_send_ft_assoc_response, */ wlan_hdd_set_tx_flow_info(); hdd_place_marker(adapter, "ASSOCIATION COMPLETE", NULL); if (policy_mgr_is_mcc_in_24G(hdd_ctx->psoc)) { if (hdd_ctx->miracast_value) wlan_hdd_set_mas(adapter, hdd_ctx->miracast_value); } if (!is_roam) { /* Initialize the Linkup event completion variable */ INIT_COMPLETION(adapter->linkup_event_var); /* * Enable Linkup Event Servicing which allows the net * device notifier to set the linkup event variable. */ adapter->is_link_up_service_needed = true; /* Switch on the Carrier to activate the device */ wlan_hdd_netif_queue_control(adapter, WLAN_NETIF_CARRIER_ON, WLAN_CONTROL_PATH); /* * Wait for the Link to up to ensure all the queues * are set properly by the kernel. */ rc = wait_for_completion_timeout( &adapter->linkup_event_var, msecs_to_jiffies(ASSOC_LINKUP_TIMEOUT)); /* * Disable Linkup Event Servicing - no more service * required from the net device notifier call. */ adapter->is_link_up_service_needed = false; } vdev_mlme = wlan_objmgr_vdev_get_comp_private_obj(vdev, WLAN_UMAC_COMP_MLME); if (vdev_mlme) uapsd_mask = vdev_mlme->ext_vdev_ptr->connect_info.uapsd_per_ac_bitmask; cdp_hl_fc_set_td_limit(soc, link_info->vdev_id, sta_ctx->conn_info.chan_freq); hdd_wmm_assoc(adapter, false, uapsd_mask); if (!rsp->is_wps_connection && !rsp->is_osen_connection && (sta_ctx->conn_info.auth_type == eCSR_AUTH_TYPE_NONE || sta_ctx->conn_info.auth_type == eCSR_AUTH_TYPE_OPEN_SYSTEM || sta_ctx->conn_info.auth_type == eCSR_AUTH_TYPE_SHARED_KEY || hdd_cm_is_fils_connection(rsp))) is_auth_required = false; if (is_roam) /* If roaming is set check if FW roaming/LFR3 */ ucfg_mlme_get_roaming_offload(hdd_ctx->psoc, &is_roam_offload); if (is_roam_offload || !is_roam) { /* For FW_ROAM/LFR3 OR connect */ /* for LFR 3 get authenticated info from resp */ if (is_roam) { is_auth_required = hdd_cm_is_roam_auth_required(sta_ctx, rsp); if (is_auth_required) wlan_acquire_peer_key_wakelock(hdd_ctx->pdev, rsp->bssid.bytes); } hdd_debug("is_roam_offload %d, is_roam %d, is_auth_required %d", is_roam_offload, is_roam, is_auth_required); hdd_roam_register_sta(link_info, &rsp->bssid, is_auth_required); } else { /* for host roam/LFR2 */ hdd_cm_set_peer_authenticate(link_info, &rsp->bssid, is_auth_required); } hdd_debug("Enabling queues"); hdd_cm_netif_queue_enable(adapter); /* send peer status indication to oem app */ if (vdev_mlme) { hdd_send_peer_status_ind_to_app( &rsp->bssid, ePeerConnected, vdev_mlme->ext_vdev_ptr->connect_info.timing_meas_cap, link_info->vdev_id, &vdev_mlme->ext_vdev_ptr->connect_info.chan_info, adapter->device_mode); } if (ucfg_ipa_is_enabled() && !is_auth_required) { status = hdd_ipa_get_tx_pipe(hdd_ctx, link_info, &alt_pipe); if (!QDF_IS_STATUS_SUCCESS(status)) { hdd_debug("Failed to get alternate pipe for vdev %d", link_info->vdev_id); alt_pipe = false; } ucfg_ipa_wlan_evt(hdd_ctx->pdev, adapter->dev, adapter->device_mode, link_info->vdev_id, WLAN_IPA_STA_CONNECT, rsp->bssid.bytes, alt_pipe); } if (adapter->device_mode == QDF_STA_MODE) cdp_reset_rx_hw_ext_stats(soc); wlan_hdd_auto_shutdown_enable(hdd_ctx, false); DPTRACE(qdf_dp_trace_mgmt_pkt(QDF_DP_TRACE_MGMT_PACKET_RECORD, link_info->vdev_id, QDF_TRACE_DEFAULT_PDEV_ID, QDF_PROTO_TYPE_MGMT, QDF_PROTO_MGMT_ASSOC)); if (is_roam) ucfg_dp_nud_indicate_roam(vdev); /* hdd_objmgr_set_peer_mlme_auth_state */ if (adapter->keep_alive_interval) hdd_vdev_send_sta_keep_alive_interval(link_info, hdd_ctx, adapter->keep_alive_interval); } static void hdd_cm_connect_success_post_user_update(struct wlan_objmgr_vdev *vdev, struct wlan_cm_connect_resp *rsp) { struct hdd_context *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); struct hdd_adapter *adapter; struct wlan_hdd_link_info *link_info; bool is_roam = rsp->is_reassoc; if (!hdd_ctx) { hdd_err("hdd_ctx is NULL"); return; } link_info = hdd_get_link_info_by_vdev(hdd_ctx, rsp->vdev_id); if (!link_info) { hdd_err("adapter is NULL for vdev %d", rsp->vdev_id); return; } if (!is_roam) { /* call only for connect */ qdf_runtime_pm_allow_suspend(&hdd_ctx->runtime_context.connect); hdd_allow_suspend(WIFI_POWER_EVENT_WAKELOCK_CONNECT); } adapter = link_info->adapter; hdd_cm_clear_pmf_stats(adapter); if (adapter->device_mode == QDF_STA_MODE) { /* Inform FTM TIME SYNC about the connection with AP */ hdd_ftm_time_sync_sta_state_notify(adapter, FTM_TIME_SYNC_STA_CONNECTED); ucfg_mlme_init_twt_context(hdd_ctx->psoc, &rsp->bssid, TWT_ALL_SESSIONS_DIALOG_ID); ucfg_twt_init_context(hdd_ctx->psoc, &rsp->bssid, TWT_ALL_SESSIONS_DIALOG_ID); } ucfg_dp_periodic_sta_stats_start(vdev); wlan_twt_concurrency_update(hdd_ctx); if (wlan_vdev_mlme_is_mlo_link_switch_in_progress(vdev)) hdd_send_ps_config_to_fw(adapter); } static void hdd_cm_connect_success(struct wlan_objmgr_vdev *vdev, struct wlan_cm_connect_resp *rsp, enum osif_cb_type type) { switch (type) { case OSIF_PRE_USERSPACE_UPDATE: hdd_cm_connect_success_pre_user_update(vdev, rsp); break; case OSIF_POST_USERSPACE_UPDATE: hdd_cm_connect_success_post_user_update(vdev, rsp); break; default: hdd_cm_connect_success_pre_user_update(vdev, rsp); hdd_cm_connect_success_post_user_update(vdev, rsp); } } #if defined(WLAN_FEATURE_11BE_MLO) && defined(CFG80211_11BE_BASIC) void hdd_cm_connect_active_notify(uint8_t vdev_id) { struct hdd_context *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); struct wlan_hdd_link_info *link_info; if (!hdd_ctx) { hdd_err("HDD context is NULL"); return; } link_info = hdd_get_link_info_by_vdev(hdd_ctx, vdev_id); if (!link_info) { hdd_err("Link info not found for vdev %d", vdev_id); return; } if (hdd_adapter_restore_link_vdev_map(link_info->adapter, true)) hdd_adapter_update_mlo_mgr_mac_addr(link_info->adapter); } #endif QDF_STATUS hdd_cm_connect_complete(struct wlan_objmgr_vdev *vdev, struct wlan_cm_connect_resp *rsp, enum osif_cb_type type) { if (QDF_IS_STATUS_ERROR(rsp->connect_status)) hdd_cm_connect_failure(vdev, rsp, type); else hdd_cm_connect_success(vdev, rsp, type); return QDF_STATUS_SUCCESS; } QDF_STATUS hdd_cm_send_vdev_keys(struct wlan_objmgr_vdev *vdev, u8 key_index, bool pairwise, enum wlan_crypto_cipher_type cipher_type) { return wlan_hdd_send_key_vdev(vdev, key_index, pairwise, cipher_type); } #ifdef WLAN_VENDOR_HANDOFF_CONTROL #define WLAN_WAIT_TIME_HANDOFF_PARAMS 1000 QDF_STATUS hdd_cm_get_handoff_param(struct wlan_objmgr_psoc *psoc, uint8_t vdev_id, enum vendor_control_roam_param param_id) { QDF_STATUS status; int retval; void *vendor_handoff_context; struct osif_request *request; static const struct osif_request_params params = { .priv_size = 0, .timeout_ms = WLAN_WAIT_TIME_HANDOFF_PARAMS, }; request = osif_request_alloc(¶ms); if (!request) { hdd_err("Request allocation failure"); status = QDF_STATUS_E_NOMEM; goto error; } vendor_handoff_context = osif_request_cookie(request); hdd_debug("sending vendor handoff param request for :0x%x", param_id); status = ucfg_cm_roam_send_vendor_handoff_param_req(psoc, vdev_id, param_id, vendor_handoff_context); if (QDF_IS_STATUS_ERROR(status)) { hdd_err("Unable to get vendor handoff param"); goto error; } retval = osif_request_wait_for_response(request); if (retval) { hdd_err("Target response timed out"); status = qdf_status_from_os_return(retval); } error: if (request) osif_request_put(request); return status; } QDF_STATUS hdd_cm_get_vendor_handoff_params(struct wlan_objmgr_psoc *psoc, void *vendor_handoff_context) { struct osif_request *request; hdd_debug("Received vendor handoff event from FW"); request = osif_request_get(vendor_handoff_context); if (!request) { hdd_err("Invalid request"); return QDF_STATUS_E_FAILURE; } osif_request_complete(request); osif_request_put(request); hdd_exit(); return QDF_STATUS_SUCCESS; } #endif QDF_STATUS hdd_cm_get_scan_ie_params(struct wlan_objmgr_vdev *vdev, struct element_info *scan_ie, enum dot11_mode_filter *dot11mode_filter) { struct hdd_context *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); struct wlan_hdd_link_info *link_info; struct hdd_scan_info *scan_info; if (!hdd_ctx) { hdd_err("hdd_ctx is NULL"); return QDF_STATUS_E_INVAL; } link_info = hdd_get_link_info_by_vdev(hdd_ctx, wlan_vdev_get_id(vdev)); if (!link_info) { hdd_err("adapter is NULL for vdev %d", wlan_vdev_get_id(vdev)); return QDF_STATUS_E_INVAL; } scan_info = &link_info->adapter->scan_info; if (link_info->adapter->device_mode == QDF_P2P_CLIENT_MODE) { scan_ie->ptr = &scan_info->scan_add_ie.addIEdata[0]; scan_ie->len = scan_info->scan_add_ie.length; } else if (scan_info->default_scan_ies) { scan_ie->ptr = scan_info->default_scan_ies; scan_ie->len = scan_info->default_scan_ies_len; } else if (scan_info->scan_add_ie.length) { scan_ie->ptr = scan_info->scan_add_ie.addIEdata; scan_ie->len = scan_info->scan_add_ie.length; } *dot11mode_filter = hdd_get_dot11mode_filter(hdd_ctx); return QDF_STATUS_SUCCESS; } #ifdef WLAN_FEATURE_ROAM_OFFLOAD #ifdef WLAN_FEATURE_FILS_SK QDF_STATUS hdd_cm_save_gtk(struct wlan_objmgr_vdev *vdev, struct wlan_cm_connect_resp *rsp) { uint8_t *kek; uint32_t kek_len; uint8_t *kck = NULL; uint8_t kck_len = 0; uint8_t replay_ctr_def[REPLAY_CTR_LEN] = {0}; uint8_t *replay_ctr; struct hdd_context *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); struct wlan_hdd_link_info *link_info; if (!hdd_ctx) { hdd_err("hdd_ctx is NULL"); return QDF_STATUS_E_INVAL; } link_info = hdd_get_link_info_by_vdev(hdd_ctx, rsp->vdev_id); if (!link_info) { hdd_err("adapter is NULL for vdev %d", rsp->vdev_id); return QDF_STATUS_E_INVAL; } if (rsp->is_reassoc && rsp->roaming_info) { kek = rsp->roaming_info->kek; kek_len = rsp->roaming_info->kek_len; kck = rsp->roaming_info->kck; kck_len = rsp->roaming_info->kck_len; replay_ctr = rsp->roaming_info->replay_ctr; } else if (rsp->connect_ies.fils_ie) { kek = rsp->connect_ies.fils_ie->kek; kek_len = rsp->connect_ies.fils_ie->kek_len; replay_ctr = replay_ctr_def; } else { return QDF_STATUS_SUCCESS; } wlan_hdd_save_gtk_offload_params(link_info->adapter, kck, kck_len, kek, kek_len, replay_ctr, true); return QDF_STATUS_SUCCESS; } #else QDF_STATUS hdd_cm_save_gtk(struct wlan_objmgr_vdev *vdev, struct wlan_cm_connect_resp *rsp) { uint8_t *kek; uint32_t kek_len; uint8_t *kck = NULL; uint8_t kck_len = 0; uint8_t *replay_ctr; struct hdd_context *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); struct wlan_hdd_link_info *link_info; if (!hdd_ctx) { hdd_err("hdd_ctx is NULL"); return QDF_STATUS_E_INVAL; } link_info = hdd_get_link_info_by_vdev(hdd_ctx, rsp->vdev_id); if (!link_info) { hdd_err("adapter is NULL for vdev %d", rsp->vdev_id); return QDF_STATUS_E_INVAL; } if (rsp->is_reassoc && rsp->roaming_info) { kek = rsp->roaming_info.kek; kek_len = rsp->roaming_info.kek_len; kck = rsp->roaming_info.kck; kck_len = rsp->roaming_info.kck_len; replay_ctr = rsp->roaming_info.replay_ctr; } else { return QDF_STATUS_SUCCESS; } wlan_hdd_save_gtk_offload_params(link_info->adapter, kck, kck_len, kek, kek_len, replay_ctr, true); return QDF_STATUS_SUCCESS; } #endif /* WLAN_FEATURE_FILS_SK*/ #else QDF_STATUS hdd_cm_save_gtk(struct wlan_objmgr_vdev *vdev, struct wlan_cm_connect_resp *rsp) { return QDF_STATUS_SUCCESS; } #endif #ifdef WLAN_FEATURE_FILS_SK static void hdd_update_hlp_info(struct net_device *dev, struct wlan_cm_connect_resp *rsp) { struct sk_buff *skb; uint16_t skb_len; struct llc_snap_hdr_t *llc_hdr; QDF_STATUS status; uint8_t *hlp_data; uint16_t hlp_data_len; struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); if (!rsp || !rsp->connect_ies.fils_ie) { return; } if (!rsp->connect_ies.fils_ie->hlp_data_len) { return; } hlp_data = rsp->connect_ies.fils_ie->hlp_data; hlp_data_len = rsp->connect_ies.fils_ie->hlp_data_len; /* Calculate skb length */ skb_len = (2 * ETH_ALEN) + hlp_data_len; skb = qdf_nbuf_alloc(NULL, skb_len, 0, 4, false); if (!skb) { hdd_err("HLP packet nbuf alloc fails"); return; } qdf_mem_copy(skb_put(skb, ETH_ALEN), rsp->connect_ies.fils_ie->dst_mac.bytes, QDF_MAC_ADDR_SIZE); qdf_mem_copy(skb_put(skb, ETH_ALEN), rsp->connect_ies.fils_ie->src_mac.bytes, QDF_MAC_ADDR_SIZE); llc_hdr = (struct llc_snap_hdr_t *)hlp_data; if (IS_SNAP(llc_hdr)) { hlp_data += LLC_SNAP_HDR_OFFSET_ETHERTYPE; hlp_data_len += LLC_SNAP_HDR_OFFSET_ETHERTYPE; } qdf_mem_copy(skb_put(skb, hlp_data_len), hlp_data, hlp_data_len); /* * This HLP packet is formed from HLP info encapsulated * in assoc response frame which is AEAD encrypted. * Hence, this checksum validation can be set unnecessary. * i.e. network layer need not worry about checksum. */ skb->ip_summed = CHECKSUM_UNNECESSARY; /* * adapter->deflink->vdev is directly dereferenced because in per packet * path usage of hdd_get_vdev_by_user is costly operation as it * involves lock access. And it is guaranteed during TX/RX operations * vdev will be active will not deleted. */ status = ucfg_dp_rx_packet_cbk(adapter->deflink->vdev, (qdf_nbuf_t)skb); if (QDF_IS_STATUS_ERROR(status)) { hdd_err("Sending HLP packet fails"); return; } hdd_debug("send HLP packet to netif successfully"); } QDF_STATUS hdd_cm_set_hlp_data(struct net_device *dev, struct wlan_objmgr_vdev *vdev, struct wlan_cm_connect_resp *rsp) { hdd_update_hlp_info(dev, rsp); return QDF_STATUS_SUCCESS; } #endif #ifdef WLAN_FEATURE_PREAUTH_ENABLE /** * hdd_cm_get_ft_preauth_response() - get ft preauth response * related information * @vdev: vdev pointer * @rsp: preauth response * @ft_ie: ft ie * @ft_ie_ip_len: ft ie ip length * @ft_ie_length: ft ies length * * This function is used to get ft ie related information * * Return: none */ static void hdd_cm_get_ft_preauth_response(struct wlan_objmgr_vdev *vdev, struct wlan_preauth_rsp *rsp, uint8_t *ft_ie, uint32_t ft_ie_ip_len, uint16_t *ft_ie_length) { struct mlme_legacy_priv *mlme_priv; *ft_ie_length = 0; if (!vdev) return; mlme_priv = wlan_vdev_mlme_get_ext_hdl(vdev); if (!mlme_priv) return; /* All or nothing - proceed only if both BSSID and FT IE fit */ if ((QDF_MAC_ADDR_SIZE + rsp->ft_ie_length) > ft_ie_ip_len) return; /* * hdd needs to pack the bssid also along with the * auth response to supplicant */ qdf_mem_copy(ft_ie, rsp->pre_auth_bssid.bytes, QDF_MAC_ADDR_SIZE); /* Copy the auth resp FTIEs */ qdf_mem_copy(&ft_ie[QDF_MAC_ADDR_SIZE], rsp->ft_ie, rsp->ft_ie_length); *ft_ie_length = QDF_MAC_ADDR_SIZE + rsp->ft_ie_length; hdd_debug("Filled auth resp: %d", *ft_ie_length); } #if defined(KERNEL_SUPPORT_11R_CFG80211) QDF_STATUS hdd_cm_ft_preauth_complete(struct wlan_objmgr_vdev *vdev, struct wlan_preauth_rsp *rsp) { mac_handle_t mac_handle; struct vdev_osif_priv *osif_priv = wlan_vdev_get_ospriv(vdev); struct wireless_dev *wdev; uint16_t auth_resp_len = 0; uint32_t ric_ies_length = 0; struct cfg80211_ft_event_params ft_event = {0}; uint8_t ft_ie[DOT11F_IE_FTINFO_MAX_LEN] = {0}; uint8_t ric_ies[DOT11F_IE_RICDESCRIPTOR_MAX_LEN] = {0}; mac_handle = cds_get_context(QDF_MODULE_ID_SME); if (!mac_handle) { hdd_err("mac_handle is null"); return QDF_STATUS_E_INVAL; } if (!osif_priv) { hdd_err("Invalid vdev osif priv"); return QDF_STATUS_E_INVAL; } wdev = osif_priv->wdev; if (!wdev) { hdd_err("wdev is null"); return QDF_STATUS_E_INVAL; } if (rsp->ric_ies_length && rsp->ric_ies_length <= DOT11F_IE_RICDESCRIPTOR_MAX_LEN) { qdf_mem_copy(ric_ies, rsp->ric_ies, rsp->ric_ies_length); ric_ies_length = rsp->ric_ies_length; } else { hdd_warn("Do not send RIC IEs as length is 0"); } if (ric_ies_length) { ft_event.ric_ies = ric_ies; ft_event.ric_ies_len = ric_ies_length; } hdd_debug("RIC IEs is of length %d", ric_ies_length); hdd_cm_get_ft_preauth_response(vdev, rsp, ft_ie, DOT11F_IE_FTINFO_MAX_LEN, &auth_resp_len); if (!auth_resp_len) { hdd_debug("AuthRsp FTIES is of length 0"); return QDF_STATUS_E_FAILURE; } ucfg_cm_set_ft_pre_auth_state(vdev, true); ft_event.target_ap = ft_ie; ft_event.ies = (u8 *)(ft_ie + QDF_MAC_ADDR_SIZE); ft_event.ies_len = auth_resp_len - QDF_MAC_ADDR_SIZE; hdd_debug("ftEvent.ies_len %zu", ft_event.ies_len); hdd_debug("ftEvent.ric_ies_len %zu", ft_event.ric_ies_len); hdd_debug("ftEvent.target_ap %2x-%2x-%2x-%2x-%2x-%2x", ft_event.target_ap[0], ft_event.target_ap[1], ft_event.target_ap[2], ft_event.target_ap[3], ft_event.target_ap[4], ft_event.target_ap[5]); (void)cfg80211_ft_event(wdev->netdev, &ft_event); return QDF_STATUS_SUCCESS; } #else QDF_STATUS hdd_cm_ft_preauth_complete(struct wlan_objmgr_vdev *vdev, struct wlan_preauth_rsp *rsp) { struct vdev_osif_priv *osif_priv = wlan_vdev_get_ospriv(vdev); struct wireless_dev *wdev; uint16_t auth_resp_len = 0; uint32_t ric_ies_length = 0; char *buff; union iwreq_data wrqu; uint16_t str_len; if (!osif_priv) { hdd_err("Invalid vdev osif priv"); return QDF_STATUS_E_INVAL; } wdev = osif_priv->wdev; if (!wdev) { hdd_err("wdev is null"); return QDF_STATUS_E_INVAL; } /* need to send the IEs to the supplicant */ buff = qdf_mem_malloc(IW_CUSTOM_MAX); if (!buff) return QDF_STATUS_E_NOMEM; /* need to send the RIC IEs first */ str_len = strlcpy(buff, "RIC=", IW_CUSTOM_MAX); if (rsp->ric_ies_length && (rsp->ric_ies_length <= (IW_CUSTOM_MAX - str_len))) { qdf_mem_copy(&buff[str_len], rsp->ric_ies, rsp->ric_ies_length); ric_ies_length = rsp->ric_ies_length; wrqu.data.length = str_len + ric_ies_length; hdd_wext_send_event(wdev->netdev, IWEVCUSTOM, &wrqu, buff); } else { hdd_warn("Do not send RIC IEs as length is 0"); } /* need to provide the Auth Resp */ qdf_mem_zero(buff, IW_CUSTOM_MAX); str_len = strlcpy(buff, "AUTH=", IW_CUSTOM_MAX); hdd_cm_get_ft_preauth_response(vdev, rsp, &buff[str_len], (IW_CUSTOM_MAX - str_len), &auth_resp_len); if (!auth_resp_len) { qdf_mem_free(buff); hdd_debug("AuthRsp FTIES is of length 0"); return QDF_STATUS_E_FAILURE; } wrqu.data.length = str_len + auth_resp_len; hdd_wext_send_event(wdev->netdev, IWEVCUSTOM, &wrqu, buff); qdf_mem_free(buff); return QDF_STATUS_SUCCESS; } #endif #ifdef FEATURE_WLAN_ESE QDF_STATUS hdd_cm_cckm_preauth_complete(struct wlan_objmgr_vdev *vdev, struct wlan_preauth_rsp *rsp) { struct vdev_osif_priv *osif_priv = wlan_vdev_get_ospriv(vdev); struct wireless_dev *wdev; union iwreq_data wrqu; char buf[IW_CUSTOM_MAX + 1]; char *pos = buf; int nbytes = 0, freebytes = IW_CUSTOM_MAX; if (!osif_priv) { hdd_err("Invalid vdev osif priv"); return QDF_STATUS_E_INVAL; } wdev = osif_priv->wdev; if (!wdev) { hdd_err("wdev is null"); return QDF_STATUS_E_INVAL; } /* create the event */ memset(&wrqu, '\0', sizeof(wrqu)); memset(buf, '\0', sizeof(buf)); /* timestamp0 is lower 32 bits and timestamp1 is upper 32 bits */ hdd_debug("CCXPREAUTHNOTIFY=" QDF_MAC_ADDR_FMT " %d:%d", QDF_MAC_ADDR_REF(rsp->pre_auth_bssid.bytes), rsp->timestamp[0], rsp->timestamp[1]); nbytes = snprintf(pos, freebytes, "CCXPREAUTHNOTIFY="); pos += nbytes; freebytes -= nbytes; qdf_mem_copy(pos, rsp->pre_auth_bssid.bytes, QDF_MAC_ADDR_SIZE); pos += QDF_MAC_ADDR_SIZE; freebytes -= QDF_MAC_ADDR_SIZE; nbytes = snprintf(pos, freebytes, " %u:%u", rsp->timestamp[0], rsp->timestamp[1]); freebytes -= nbytes; wrqu.data.pointer = buf; wrqu.data.length = (IW_CUSTOM_MAX - freebytes); /* send the event */ hdd_wext_send_event(wdev->netdev, IWEVCUSTOM, &wrqu, buf); return QDF_STATUS_SUCCESS; } #endif /* FEATURE_WLAN_ESE */ #endif /* WLAN_FEATURE_PREAUTH_ENABLE */