/* * Copyright (c) 2012-2021 The Linux Foundation. All rights reserved. * Copyright (c) 2022-2023 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: wlan_hdd_scan.c * * WLAN Host Device Driver scan implementation */ #include #include #include "wlan_hdd_includes.h" #include "cds_api.h" #include "cds_api.h" #include "ani_global.h" #include "dot11f.h" #include "cds_sched.h" #include "osif_sync.h" #include "wlan_hdd_p2p.h" #include "wlan_hdd_trace.h" #include "wlan_hdd_scan.h" #include "wlan_policy_mgr_api.h" #include "wlan_hdd_power.h" #include "wma_api.h" #include "cds_utils.h" #include "wlan_p2p_ucfg_api.h" #include "cfg_ucfg_api.h" #include #include #include "wlan_utility.h" #include "wlan_hdd_object_manager.h" #include "nan_ucfg_api.h" #define SCAN_DONE_EVENT_BUF_SIZE 4096 #define RATE_MASK 0x7f /** * hdd_vendor_scan_callback() - Scan completed callback event * @adapter: HDD adapter * @req: Scan request * @aborted: true scan aborted false scan success * * This function sends scan completed callback event to NL. * * Return: none */ static void hdd_vendor_scan_callback(struct hdd_adapter *adapter, struct cfg80211_scan_request *req, bool aborted) { struct hdd_context *hddctx = WLAN_HDD_GET_CTX(adapter); struct sk_buff *skb; struct nlattr *attr; int i; uint8_t scan_status; uint64_t cookie; enum qca_nl80211_vendor_subcmds_index index = QCA_NL80211_VENDOR_SUBCMD_SCAN_DONE_INDEX; hdd_enter(); if (WLAN_HDD_ADAPTER_MAGIC != adapter->magic) { hdd_err("Invalid adapter magic"); qdf_mem_free(req); return; } skb = wlan_cfg80211_vendor_event_alloc(hddctx->wiphy, &adapter->wdev, SCAN_DONE_EVENT_BUF_SIZE + 4 + NLMSG_HDRLEN, index, GFP_KERNEL); if (!skb) { hdd_err("skb alloc failed"); qdf_mem_free(req); return; } cookie = (uintptr_t)req; attr = nla_nest_start(skb, QCA_WLAN_VENDOR_ATTR_SCAN_SSIDS); if (!attr) goto nla_put_failure; for (i = 0; i < req->n_ssids; i++) { if (nla_put(skb, i, req->ssids[i].ssid_len, req->ssids[i].ssid)) { hdd_err("Failed to add ssid"); goto nla_put_failure; } } nla_nest_end(skb, attr); attr = nla_nest_start(skb, QCA_WLAN_VENDOR_ATTR_SCAN_FREQUENCIES); if (!attr) goto nla_put_failure; for (i = 0; i < req->n_channels; i++) { if (nla_put_u32(skb, i, req->channels[i]->center_freq)) { hdd_err("Failed to add channel"); goto nla_put_failure; } } nla_nest_end(skb, attr); if (req->ie && nla_put(skb, QCA_WLAN_VENDOR_ATTR_SCAN_IE, req->ie_len, req->ie)) { hdd_err("Failed to add scan ie"); goto nla_put_failure; } if (req->flags && nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_SCAN_FLAGS, req->flags)) { hdd_err("Failed to add scan flags"); goto nla_put_failure; } if (hdd_wlan_nla_put_u64(skb, QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE, cookie)) { hdd_err("Failed to add scan cookie"); goto nla_put_failure; } scan_status = (aborted == true) ? VENDOR_SCAN_STATUS_ABORTED : VENDOR_SCAN_STATUS_NEW_RESULTS; if (nla_put_u8(skb, QCA_WLAN_VENDOR_ATTR_SCAN_STATUS, scan_status)) { hdd_err("Failed to add scan status"); goto nla_put_failure; } wlan_cfg80211_vendor_event(skb, GFP_KERNEL); hdd_info("scan complete event sent to NL"); qdf_mem_free(req); return; nla_put_failure: wlan_cfg80211_vendor_free_skb(skb); qdf_mem_free(req); } #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0)) /** * hdd_cfg80211_scan_done() - Scan completed callback to cfg80211 * @adapter: Pointer to the adapter * @req : Scan request * @aborted : true scan aborted false scan success * * This function notifies scan done to cfg80211 * * Return: none */ static void hdd_cfg80211_scan_done(struct hdd_adapter *adapter, struct cfg80211_scan_request *req, bool aborted) { struct cfg80211_scan_info info = { .aborted = aborted }; if (adapter->dev->flags & IFF_UP) cfg80211_scan_done(req, &info); } #elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)) /** * hdd_cfg80211_scan_done() - Scan completed callback to cfg80211 * @adapter: Pointer to the adapter * @req : Scan request * @aborted : true scan aborted false scan success * * This function notifies scan done to cfg80211 * * Return: none */ static void hdd_cfg80211_scan_done(struct hdd_adapter *adapter, struct cfg80211_scan_request *req, bool aborted) { if (adapter->dev->flags & IFF_UP) cfg80211_scan_done(req, aborted); } #else /** * hdd_cfg80211_scan_done() - Scan completed callback to cfg80211 * @adapter: Pointer to the adapter * @req : Scan request * @aborted : true scan aborted false scan success * * This function notifies scan done to cfg80211 * * Return: none */ static void hdd_cfg80211_scan_done(struct hdd_adapter *adapter, struct cfg80211_scan_request *req, bool aborted) { cfg80211_scan_done(req, aborted); } #endif #ifdef FEATURE_WLAN_AP_AP_ACS_OPTIMIZE /** * wlan_hdd_sap_skip_scan_check() - The function will check OBSS * scan skip or not for SAP. * @hdd_ctx: pointer to hdd context. * @request: pointer to scan request. * * This function will check the scan request's chan list against the * previous ACS scan chan list. If all the chan are covered by * previous ACS scan, we can skip the scan and return scan complete * to save the SAP starting time. * * Return: true to skip the scan, * false to continue the scan */ static bool wlan_hdd_sap_skip_scan_check(struct hdd_context *hdd_ctx, struct cfg80211_scan_request *request) { int i, j; bool skip; hdd_debug("HDD_ACS_SKIP_STATUS = %d", hdd_ctx->skip_acs_scan_status); if (hdd_ctx->skip_acs_scan_status != eSAP_SKIP_ACS_SCAN) return false; qdf_spin_lock(&hdd_ctx->acs_skip_lock); if (!hdd_ctx->last_acs_freq_list || hdd_ctx->num_of_channels == 0 || request->n_channels == 0) { qdf_spin_unlock(&hdd_ctx->acs_skip_lock); return false; } skip = true; for (i = 0; i < request->n_channels ; i++) { bool find = false; for (j = 0; j < hdd_ctx->num_of_channels; j++) { if (hdd_ctx->last_acs_freq_list[j] == request->channels[i]->center_freq) { find = true; break; } } if (!find) { skip = false; hdd_debug("Freq %d isn't in ACS freq list", request->channels[i]->center_freq); break; } } qdf_spin_unlock(&hdd_ctx->acs_skip_lock); return skip; } #else static bool wlan_hdd_sap_skip_scan_check(struct hdd_context *hdd_ctx, struct cfg80211_scan_request *request) { return false; } #endif void wlan_hdd_cfg80211_scan_block(struct hdd_adapter *adapter) { struct cfg80211_scan_request *request; struct scan_req *blocked_scan_req; qdf_list_node_t *node; if (adapter->magic != WLAN_HDD_ADAPTER_MAGIC) { hdd_err("HDD adapter context is invalid"); return; } qdf_mutex_acquire(&adapter->blocked_scan_request_q_lock); while (!qdf_list_empty(&adapter->blocked_scan_request_q)) { qdf_list_remove_front(&adapter->blocked_scan_request_q, &node); blocked_scan_req = qdf_container_of(node, struct scan_req, node); request = blocked_scan_req->scan_request; request->n_ssids = 0; request->n_channels = 0; if (blocked_scan_req->source == NL_SCAN) { hdd_err("Scan aborted. Null result sent"); hdd_cfg80211_scan_done(adapter, request, true); } else { hdd_err("Vendor scan aborted. Null result sent"); hdd_vendor_scan_callback(adapter, request, true); } qdf_mem_free(blocked_scan_req); } qdf_mutex_release(&adapter->blocked_scan_request_q_lock); } void hdd_init_scan_reject_params(struct hdd_context *hdd_ctx) { if (hdd_ctx) { hdd_ctx->last_scan_reject_timestamp = 0; hdd_ctx->last_scan_reject_vdev_id = WLAN_UMAC_VDEV_ID_MAX; hdd_ctx->last_scan_reject_reason = 0; hdd_ctx->scan_reject_cnt = 0; } } /* * wlan_hdd_update_scan_ies() - API to update the scan IEs of scan request * with already stored default scan IEs * * @adapter: Pointer to HDD adapter * @scan_info: Pointer to scan info in HDD adapter * @scan_ie: Pointer to scan IE in scan request * @scan_ie_len: Pointer to scan IE length in scan request * * Return: 0 on success; error number otherwise */ static int wlan_hdd_update_scan_ies(struct hdd_adapter *adapter, struct hdd_scan_info *scan_info, uint8_t *scan_ie, uint16_t *scan_ie_len) { uint16_t rem_len = scan_info->default_scan_ies_len; uint8_t *temp_ie = scan_info->default_scan_ies; uint8_t *current_ie; const uint8_t *mbo_ie; uint8_t elem_id; uint16_t elem_len; bool add_ie = false; if (!scan_info->default_scan_ies_len || !scan_info->default_scan_ies) return 0; mbo_ie = wlan_get_vendor_ie_ptr_from_oui(MBO_OUI_TYPE, MBO_OUI_TYPE_SIZE, scan_ie, *scan_ie_len); while (rem_len >= 2) { current_ie = temp_ie; elem_id = *temp_ie++; elem_len = *temp_ie++; rem_len -= 2; if (elem_len > rem_len) { hdd_err("Invalid element len %d for elem %d", elem_len, elem_id); return 0; } switch (elem_id) { case DOT11F_EID_EXTCAP: if (!wlan_get_ie_ptr_from_eid(DOT11F_EID_EXTCAP, scan_ie, *scan_ie_len)) add_ie = true; break; case WLAN_ELEMID_VENDOR: /* Donot add MBO IE if its already present */ if ((!mbo_ie && 0 == qdf_mem_cmp(&temp_ie[0], MBO_OUI_TYPE, MBO_OUI_TYPE_SIZE)) || (0 == qdf_mem_cmp(&temp_ie[0], QCN_OUI_TYPE, QCN_OUI_TYPE_SIZE))) add_ie = true; break; } if (add_ie && (((*scan_ie_len) + elem_len) > SIR_MAC_MAX_ADD_IE_LENGTH)){ hdd_err("Not enough buffer to save default scan IE's"); return 0; } if (add_ie) { qdf_mem_copy(scan_ie + (*scan_ie_len), current_ie, elem_len + 2); (*scan_ie_len) += (elem_len + 2); add_ie = false; } temp_ie += elem_len; rem_len -= elem_len; } return 0; } static int wlan_hdd_enqueue_blocked_scan_request(struct net_device *dev, struct cfg80211_scan_request *request, uint8_t source) { struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); struct scan_req *blocked_scan_req = qdf_mem_malloc(sizeof(*blocked_scan_req)); int ret = 0; if (!blocked_scan_req) return -EINVAL; blocked_scan_req->dev = dev; blocked_scan_req->scan_request = request; blocked_scan_req->source = source; blocked_scan_req->scan_id = 0; qdf_mutex_acquire(&adapter->blocked_scan_request_q_lock); if (qdf_list_size(&adapter->blocked_scan_request_q) < WLAN_MAX_SCAN_COUNT) qdf_list_insert_back(&adapter->blocked_scan_request_q, &blocked_scan_req->node); else ret = -EINVAL; qdf_mutex_release(&adapter->blocked_scan_request_q_lock); if (ret) { hdd_err("Maximum number of block scan request reached!"); qdf_mem_free(blocked_scan_req); } return ret; } /* Define short name to use in cds_trigger_recovery */ #define SCAN_FAILURE QDF_SCAN_ATTEMPT_FAILURES /** * __wlan_hdd_cfg80211_scan() - API to process cfg80211 scan request * @wiphy: Pointer to wiphy * @request: Pointer to scan request * @source: scan request source(NL/Vendor scan) * * This API responds to scan trigger and update cfg80211 scan database * later, scan dump command can be used to receive scan results * * Return: 0 for success, non zero for failure */ static int __wlan_hdd_cfg80211_scan(struct wiphy *wiphy, struct cfg80211_scan_request *request, uint8_t source) { struct net_device *dev = request->wdev->netdev; struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); int status; struct hdd_scan_info *scan_info = NULL; struct hdd_adapter *con_sap_adapter; struct hdd_ap_ctx *ap_ctx; qdf_freq_t con_dfs_ch_freq; uint8_t curr_vdev_id; enum scan_reject_states curr_reason; static uint32_t scan_ebusy_cnt; struct scan_params params = {0}; bool self_recovery; struct wlan_objmgr_vdev *vdev; QDF_STATUS qdf_status; bool enable_connected_scan; enum phy_ch_width con_dfs_ch_width; if (cds_is_fw_down()) { hdd_err("firmware is down, scan cmd cannot be processed"); return -EINVAL; } 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; status = wlan_hdd_validate_context(hdd_ctx); if (0 != status) return status; qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD, TRACE_CODE_HDD_CFG80211_SCAN, adapter->deflink->vdev_id, request->n_channels); if (!sme_is_session_id_valid(hdd_ctx->mac_handle, adapter->deflink->vdev_id)) return -EINVAL; qdf_status = ucfg_mlme_get_self_recovery(hdd_ctx->psoc, &self_recovery); if (QDF_IS_STATUS_ERROR(qdf_status)) { hdd_err("Failed to get self recovery ini config"); return -EIO; } enable_connected_scan = ucfg_scan_is_connected_scan_enabled( hdd_ctx->psoc); if (!enable_connected_scan && hdd_cm_is_vdev_associated(adapter->deflink)) { hdd_info("enable_connected_scan is false, Aborting scan"); if (wlan_hdd_enqueue_blocked_scan_request(dev, request, source)) return -EAGAIN; schedule_work(&adapter->scan_block_work); return 0; } /* * NDI and monitor mode don't need scan from userspace to establish * connection and it does not support scan request either. */ if (QDF_NDI_MODE == adapter->device_mode || QDF_MONITOR_MODE == adapter->device_mode) { hdd_err("Scan not supported for %s", qdf_opmode_str(adapter->device_mode)); return -EINVAL; } scan_info = &adapter->scan_info; /* Block All Scan during DFS operation and send null scan result */ con_sap_adapter = hdd_get_con_sap_adapter(adapter, true); if (con_sap_adapter) { ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(con_sap_adapter->deflink); con_dfs_ch_freq = ap_ctx->sap_config.chan_freq; con_dfs_ch_width = ap_ctx->sap_config.ch_params.ch_width; if (con_dfs_ch_freq == AUTO_CHANNEL_SELECT) con_dfs_ch_freq = ap_ctx->operating_chan_freq; if (!policy_mgr_is_hw_dbs_capable(hdd_ctx->psoc) && !policy_mgr_is_sta_sap_scc_allowed_on_dfs_chan( hdd_ctx->psoc) && (wlan_reg_is_dfs_for_freq(hdd_ctx->pdev, con_dfs_ch_freq) || (wlan_reg_is_5ghz_ch_freq(con_dfs_ch_freq) && con_dfs_ch_width == CH_WIDTH_160MHZ))) { /* Provide empty scan result during DFS operation since * scanning not supported during DFS. Reason is * following case: * DFS is supported only in SCC for MBSSID Mode. * We shall not return EBUSY or ENOTSUPP as when Primary * AP is operating in DFS channel and secondary AP is * started. Though we force SCC in driver, the hostapd * issues obss scan before starting secAP. This results * in MCC in DFS mode. Thus we return null scan result. * If we return scan failure hostapd fails secondary AP * startup. */ hdd_err("##In DFS Master mode. Scan aborted"); if (wlan_hdd_enqueue_blocked_scan_request(dev, request, source)) return -EAGAIN; schedule_work(&adapter->scan_block_work); return 0; } } /* Check if scan is allowed at this point of time */ if (hdd_is_connection_in_progress(&curr_vdev_id, &curr_reason)) { scan_ebusy_cnt++; hdd_err_rl("Scan not allowed. scan_ebusy_cnt: %d Session %d Reason %d", scan_ebusy_cnt, curr_vdev_id, curr_reason); if (hdd_ctx->last_scan_reject_vdev_id != curr_vdev_id || hdd_ctx->last_scan_reject_reason != curr_reason || !hdd_ctx->last_scan_reject_timestamp) { hdd_ctx->last_scan_reject_vdev_id = curr_vdev_id; hdd_ctx->last_scan_reject_reason = curr_reason; hdd_ctx->last_scan_reject_timestamp = jiffies + msecs_to_jiffies(SCAN_REJECT_THRESHOLD_TIME); hdd_ctx->scan_reject_cnt = 0; } else { hdd_ctx->scan_reject_cnt++; if ((hdd_ctx->scan_reject_cnt >= SCAN_REJECT_THRESHOLD) && qdf_system_time_after(jiffies, hdd_ctx->last_scan_reject_timestamp)) { hdd_err("scan reject threshold reached Session %d Reason %d count %d reject timestamp %lu jiffies %lu", curr_vdev_id, curr_reason, hdd_ctx->scan_reject_cnt, hdd_ctx->last_scan_reject_timestamp, jiffies); hdd_ctx->last_scan_reject_timestamp = 0; hdd_ctx->scan_reject_cnt = 0; if (cds_is_fatal_event_enabled()) { cds_flush_logs(WLAN_LOG_TYPE_FATAL, WLAN_LOG_INDICATOR_HOST_DRIVER, WLAN_LOG_REASON_SCAN_NOT_ALLOWED, false, self_recovery); } else { hdd_err("Triggering SSR due to scan stuck"); cds_trigger_recovery(SCAN_FAILURE); } } } return -EBUSY; } hdd_init_scan_reject_params(hdd_ctx); /* Check whether SAP scan can be skipped or not */ if (adapter->device_mode == QDF_SAP_MODE && wlan_hdd_sap_skip_scan_check(hdd_ctx, request)) { hdd_debug("sap scan skipped"); if (wlan_hdd_enqueue_blocked_scan_request(dev, request, source)) return -EAGAIN; schedule_work(&adapter->scan_block_work); return 0; } params.source = source; params.default_ie.len = 0; /* Store the Scan IE's in Adapter*/ if (request->ie_len) { if (request->ie_len > SIR_MAC_MAX_ADD_IE_LENGTH) { hdd_debug("Invalid ie_len: %zu", request->ie_len); return -EINVAL; } /* save this for future association (join requires this) */ memset(&scan_info->scan_add_ie, 0, sizeof(scan_info->scan_add_ie)); memcpy(scan_info->scan_add_ie.addIEdata, request->ie, request->ie_len); scan_info->scan_add_ie.length = request->ie_len; wlan_hdd_update_scan_ies(adapter, scan_info, scan_info->scan_add_ie.addIEdata, &scan_info->scan_add_ie.length); } else { if (scan_info->default_scan_ies && scan_info->default_scan_ies_len) { qdf_mem_copy(scan_info->scan_add_ie.addIEdata, scan_info->default_scan_ies, scan_info->default_scan_ies_len); scan_info->scan_add_ie.length = scan_info->default_scan_ies_len; params.default_ie.ptr = qdf_mem_malloc(scan_info->default_scan_ies_len); if (params.default_ie.ptr) { qdf_mem_copy(params.default_ie.ptr, scan_info->default_scan_ies, scan_info->default_scan_ies_len); params.default_ie.len = scan_info->default_scan_ies_len; } } } if (QDF_P2P_CLIENT_MODE == adapter->device_mode || QDF_P2P_DEVICE_MODE == adapter->device_mode) { /* Disable NAN Discovery if enabled */ ucfg_nan_disable_concurrency(hdd_ctx->psoc); } vdev = hdd_objmgr_get_vdev_by_user(adapter->deflink, WLAN_OSIF_SCAN_ID); if (!vdev) { status = -EINVAL; goto error; } if ((request->n_ssids == 1) && (request->ssids) && (request->ssids[0].ssid_len > 7) && !qdf_mem_cmp(&request->ssids[0], "DIRECT-", 7)) ucfg_p2p_status_scan(vdev); /* If this a scan on SAP adapter, use scan priority high */ if (adapter->device_mode == QDF_SAP_MODE) params.priority = SCAN_PRIORITY_HIGH; else /* Use default scan priority */ params.priority = SCAN_PRIORITY_COUNT; status = ucfg_mlme_get_scan_probe_unicast_ra( hdd_ctx->psoc, ¶ms.scan_probe_unicast_ra); if (QDF_IS_STATUS_ERROR(status)) hdd_err("Failed to get unicast probe req ra cfg"); params.mld_id = ucfg_mlme_get_eht_mld_id(hdd_ctx->psoc); hdd_debug("MLD ID: %d", params.mld_id); status = wlan_cfg80211_scan(vdev, request, ¶ms); hdd_objmgr_put_vdev_by_user(vdev, WLAN_OSIF_SCAN_ID); error: if (params.default_ie.ptr) qdf_mem_free(params.default_ie.ptr); return status; } #undef SCAN_FAILURE /** * wlan_hdd_cfg80211_scan() - API to process cfg80211 scan request * @wiphy: Pointer to wiphy * @request: Pointer to scan request * * This API responds to scan trigger and update cfg80211 scan database * later, scan dump command can be used to receive scan results * * Return: 0 for success, non zero for failure */ int wlan_hdd_cfg80211_scan(struct wiphy *wiphy, struct cfg80211_scan_request *request) { int errno; struct osif_vdev_sync *vdev_sync; errno = osif_vdev_sync_op_start(request->wdev->netdev, &vdev_sync); if (errno) return errno; errno = __wlan_hdd_cfg80211_scan(wiphy, request, NL_SCAN); osif_vdev_sync_op_stop(vdev_sync); return errno; } /** * wlan_hdd_get_rates() -API to get the rates from scan request * @wiphy: Pointer to wiphy * @band: Band * @rates: array of rates * @rate_count: number of rates * * Return: o for failure, rate bitmap for success */ static uint32_t wlan_hdd_get_rates(struct wiphy *wiphy, enum nl80211_band band, const u8 *rates, unsigned int rate_count) { uint32_t j, count, rate_bitmap = 0; uint32_t rate; bool found; for (count = 0; count < rate_count; count++) { rate = ((rates[count]) & RATE_MASK) * 5; found = false; for (j = 0; j < wiphy->bands[band]->n_bitrates; j++) { if (wiphy->bands[band]->bitrates[j].bitrate == rate) { found = true; rate_bitmap |= (1 << j); break; } } if (!found) return 0; } return rate_bitmap; } /** * wlan_hdd_send_scan_start_event() -API to send the scan start event * @wiphy: Pointer to wiphy * @wdev: Pointer to net device * @cookie: scan identifier * * Return: return 0 on success and negative error code on failure */ static int wlan_hdd_send_scan_start_event(struct wiphy *wiphy, struct wireless_dev *wdev, uint64_t cookie) { struct sk_buff *skb; int ret; enum qca_nl80211_vendor_subcmds_index index = QCA_NL80211_VENDOR_SUBCMD_SCAN_INDEX; skb = wlan_cfg80211_vendor_cmd_alloc_reply_skb(wiphy, sizeof(u64) + NLA_HDRLEN + NLMSG_HDRLEN); if (!skb) { hdd_err(" reply skb alloc failed"); return -ENOMEM; } if (hdd_wlan_nla_put_u64(skb, QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE, cookie)) { hdd_err("nla put fail"); wlan_cfg80211_vendor_free_skb(skb); return -EINVAL; } ret = wlan_cfg80211_vendor_cmd_reply(skb); /* Send a scan started event to supplicant */ skb = wlan_cfg80211_vendor_event_alloc(wiphy, wdev, sizeof(u64) + 4 + NLMSG_HDRLEN, index, GFP_KERNEL); if (!skb) { hdd_err("skb alloc failed"); return -ENOMEM; } if (hdd_wlan_nla_put_u64(skb, QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE, cookie)) { wlan_cfg80211_vendor_free_skb(skb); return -EINVAL; } wlan_cfg80211_vendor_event(skb, GFP_KERNEL); return ret; } /** * wlan_hdd_copy_bssid() - API to copy the bssid to vendor Scan request * @request: Pointer to vendor scan request * @bssid: Pointer to BSSID * * This API copies the specific BSSID received from Supplicant and copies it to * the vendor Scan request * * Return: None */ #if defined(CFG80211_SCAN_BSSID) || \ (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0)) static inline void wlan_hdd_copy_bssid(struct cfg80211_scan_request *request, uint8_t *bssid) { qdf_mem_copy(request->bssid, bssid, QDF_MAC_ADDR_SIZE); } #else static inline void wlan_hdd_copy_bssid(struct cfg80211_scan_request *request, uint8_t *bssid) { } #endif static void hdd_process_vendor_acs_response(struct hdd_adapter *adapter) { qdf_mc_timer_t *vendor_acs_timer; if (!test_bit(VENDOR_ACS_RESPONSE_PENDING, &adapter->deflink->link_flags)) { return; } vendor_acs_timer = &adapter->deflink->session.ap.vendor_acs_timer; if (QDF_TIMER_STATE_RUNNING == qdf_mc_timer_get_current_state(vendor_acs_timer)) { qdf_mc_timer_stop(vendor_acs_timer); } } #if defined(CFG80211_SCAN_RANDOM_MAC_ADDR) || \ (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0)) /** * wlan_hdd_vendor_scan_random_attr() - check and fill scan randomization attrs * @wiphy: Pointer to wiphy * @request: Pointer to scan request * @adapter: Pointer to hdd adapter * @tb: Pointer to nl attributes * * This function is invoked to check whether vendor scan needs * probe req source addr, if so populates mac_addr and mac_addr_mask * in scan request with nl attrs. * * Return: 0 - on success, negative value on failure */ static int wlan_hdd_vendor_scan_random_attr(struct wiphy *wiphy, struct cfg80211_scan_request *request, struct hdd_adapter *adapter, struct nlattr **tb) { uint32_t i; int32_t len = QDF_MAC_ADDR_SIZE; if (!(request->flags & NL80211_SCAN_FLAG_RANDOM_ADDR)) return 0; if (!(wiphy->features & NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR) || (hdd_cm_is_vdev_connected(adapter->deflink))) { hdd_err("SCAN RANDOMIZATION not supported"); return -EOPNOTSUPP; } if (!tb[QCA_WLAN_VENDOR_ATTR_SCAN_MAC] && !tb[QCA_WLAN_VENDOR_ATTR_SCAN_MAC_MASK]) { qdf_mem_zero(request->mac_addr, len); qdf_mem_zero(request->mac_addr_mask, len); request->mac_addr[0] = 0x2; request->mac_addr_mask[0] = 0x3; return 0; } if (!tb[QCA_WLAN_VENDOR_ATTR_SCAN_MAC] || !tb[QCA_WLAN_VENDOR_ATTR_SCAN_MAC_MASK]) return -EINVAL; if ((nla_len(tb[QCA_WLAN_VENDOR_ATTR_SCAN_MAC]) != len) || (nla_len(tb[QCA_WLAN_VENDOR_ATTR_SCAN_MAC_MASK]) != len)) return -EINVAL; qdf_mem_copy(request->mac_addr, nla_data(tb[QCA_WLAN_VENDOR_ATTR_SCAN_MAC]), len); qdf_mem_copy(request->mac_addr_mask, nla_data(tb[QCA_WLAN_VENDOR_ATTR_SCAN_MAC_MASK]), len); /* avoid configure on multicast address */ if (!cds_is_group_addr(request->mac_addr_mask) || cds_is_group_addr(request->mac_addr)) return -EINVAL; for (i = 0; i < ETH_ALEN; i++) request->mac_addr[i] &= request->mac_addr_mask[i]; return 0; } #else static int wlan_hdd_vendor_scan_random_attr(struct wiphy *wiphy, struct cfg80211_scan_request *request, struct hdd_adapter *adapter, struct nlattr **tb) { return 0; } #endif const struct nla_policy scan_policy[QCA_WLAN_VENDOR_ATTR_SCAN_MAX + 1] = { [QCA_WLAN_VENDOR_ATTR_SCAN_FLAGS] = {.type = NLA_U32}, [QCA_WLAN_VENDOR_ATTR_SCAN_TX_NO_CCK_RATE] = {.type = NLA_FLAG}, [QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE] = {.type = NLA_U64}, [QCA_WLAN_VENDOR_ATTR_SCAN_IE] = {.type = NLA_BINARY, .len = MAX_DEFAULT_SCAN_IE_LEN}, [QCA_WLAN_VENDOR_ATTR_SCAN_MAC] = VENDOR_NLA_POLICY_MAC_ADDR, [QCA_WLAN_VENDOR_ATTR_SCAN_MAC_MASK] = VENDOR_NLA_POLICY_MAC_ADDR, [QCA_WLAN_VENDOR_ATTR_SCAN_FREQUENCIES] = {.type = NLA_NESTED}, [QCA_WLAN_VENDOR_ATTR_SCAN_SSIDS] = {.type = NLA_NESTED}, [QCA_WLAN_VENDOR_ATTR_SCAN_SUPP_RATES] = {.type = NLA_NESTED}, [QCA_WLAN_VENDOR_ATTR_SCAN_BSSID] = {.type = NLA_BINARY}, }; /** * __wlan_hdd_cfg80211_vendor_scan() - API to process venor scan request * @wiphy: Pointer to wiphy * @wdev: Pointer to net device * @data : Pointer to the data * @data_len : length of the data * * API to process venor scan request. * * Return: return 0 on success and negative error code on failure */ static int __wlan_hdd_cfg80211_vendor_scan(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int data_len) { struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_SCAN_MAX + 1]; struct cfg80211_scan_request *request = NULL; struct nlattr *attr; enum nl80211_band band; uint32_t n_channels = 0, n_ssid = 0; uint32_t count, j; int tmp; size_t len, ie_len = 0; struct ieee80211_channel *chan; struct hdd_context *hdd_ctx = wiphy_priv(wiphy); struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(wdev->netdev); int ret; hdd_enter_dev(wdev->netdev); if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { hdd_err("Command not allowed in FTM mode"); return -EPERM; } ret = wlan_hdd_validate_context(hdd_ctx); if (ret) { /* * During SSR, if -EBUSY is returned then OBSS vendor scan is * not issued immediately. */ if (ret == -EAGAIN) return -EBUSY; return ret; } if (wlan_cfg80211_nla_parse(tb, QCA_WLAN_VENDOR_ATTR_SCAN_MAX, data, data_len, scan_policy)) { hdd_err("Invalid ATTR"); return -EINVAL; } if (tb[QCA_WLAN_VENDOR_ATTR_SCAN_FREQUENCIES]) { nla_for_each_nested(attr, tb[QCA_WLAN_VENDOR_ATTR_SCAN_FREQUENCIES], tmp) n_channels++; } else { for (band = 0; band < HDD_NUM_NL80211_BANDS; band++) if (wiphy->bands[band]) n_channels += wiphy->bands[band]->n_channels; } if (n_channels > NUM_CHANNELS) { hdd_err("Exceed max number of channels: %d", n_channels); return -EINVAL; } if (tb[QCA_WLAN_VENDOR_ATTR_SCAN_SSIDS]) nla_for_each_nested(attr, tb[QCA_WLAN_VENDOR_ATTR_SCAN_SSIDS], tmp) n_ssid++; if (MAX_SCAN_SSID < n_ssid) { hdd_err("Exceed max number of SSID: %d", n_ssid); return -EINVAL; } if (tb[QCA_WLAN_VENDOR_ATTR_SCAN_IE]) ie_len = nla_len(tb[QCA_WLAN_VENDOR_ATTR_SCAN_IE]); len = sizeof(*request) + (sizeof(*request->ssids) * n_ssid) + (sizeof(*request->channels) * n_channels) + ie_len; request = qdf_mem_malloc(len); if (!request) goto error; if (n_ssid) request->ssids = (void *)&request->channels[n_channels]; request->n_ssids = n_ssid; if (ie_len) { if (request->ssids) request->ie = (void *)(request->ssids + n_ssid); else request->ie = (void *)(request->channels + n_channels); } request->ie_len = ie_len; count = 0; if (tb[QCA_WLAN_VENDOR_ATTR_SCAN_FREQUENCIES]) { nla_for_each_nested(attr, tb[QCA_WLAN_VENDOR_ATTR_SCAN_FREQUENCIES], tmp) { if (nla_len(attr) != sizeof(uint32_t)) { hdd_err("len is not correct for frequency %d", count); goto error; } chan = ieee80211_get_channel(wiphy, nla_get_u32(attr)); if (!chan) goto error; if (chan->flags & IEEE80211_CHAN_DISABLED) continue; request->channels[count] = chan; count++; } } else { for (band = 0; band < HDD_NUM_NL80211_BANDS; band++) { if (!wiphy->bands[band]) continue; for (j = 0; j < wiphy->bands[band]->n_channels; j++) { chan = &wiphy->bands[band]->channels[j]; if (chan->flags & IEEE80211_CHAN_DISABLED) continue; request->channels[count] = chan; count++; } } } if (!count) goto error; request->n_channels = count; count = 0; if (tb[QCA_WLAN_VENDOR_ATTR_SCAN_SSIDS]) { int ssid_length; nla_for_each_nested(attr, tb[QCA_WLAN_VENDOR_ATTR_SCAN_SSIDS], tmp) { ssid_length = nla_len(attr); if ((ssid_length > WLAN_SSID_MAX_LEN) || (ssid_length < 0)) { hdd_err("SSID Len %d is not correct for network %d", ssid_length, count); goto error; } request->ssids[count].ssid_len = ssid_length; memcpy(request->ssids[count].ssid, nla_data(attr), ssid_length); count++; } } if (ie_len) nla_memcpy((void *)request->ie, tb[QCA_WLAN_VENDOR_ATTR_SCAN_IE], ie_len); for (count = 0; count < HDD_NUM_NL80211_BANDS; count++) if (wiphy->bands[count]) request->rates[count] = (1 << wiphy->bands[count]->n_bitrates) - 1; if (tb[QCA_WLAN_VENDOR_ATTR_SCAN_SUPP_RATES]) { nla_for_each_nested(attr, tb[QCA_WLAN_VENDOR_ATTR_SCAN_SUPP_RATES], tmp) { band = nla_type(attr); if (band >= HDD_NUM_NL80211_BANDS) continue; if (!wiphy->bands[band]) continue; request->rates[band] = wlan_hdd_get_rates(wiphy, band, nla_data(attr), nla_len(attr)); } } if (tb[QCA_WLAN_VENDOR_ATTR_SCAN_FLAGS]) { request->flags = nla_get_u32(tb[QCA_WLAN_VENDOR_ATTR_SCAN_FLAGS]); if ((request->flags & NL80211_SCAN_FLAG_LOW_PRIORITY) && !(wiphy->features & NL80211_FEATURE_LOW_PRIORITY_SCAN)) { hdd_err("LOW PRIORITY SCAN not supported"); goto error; } if (wlan_hdd_vendor_scan_random_attr(wiphy, request, adapter, tb)) goto error; } if (tb[QCA_WLAN_VENDOR_ATTR_SCAN_BSSID]) { if (nla_len(tb[QCA_WLAN_VENDOR_ATTR_SCAN_BSSID]) < QDF_MAC_ADDR_SIZE) { hdd_err("invalid bssid length"); goto error; } wlan_hdd_copy_bssid(request, nla_data(tb[QCA_WLAN_VENDOR_ATTR_SCAN_BSSID])); } /* Check if external acs was requested on this adapter */ hdd_process_vendor_acs_response(adapter); if (tb[QCA_WLAN_VENDOR_ATTR_SCAN_TX_NO_CCK_RATE]) request->no_cck = nla_get_flag(tb[QCA_WLAN_VENDOR_ATTR_SCAN_TX_NO_CCK_RATE]); request->wdev = wdev; request->wiphy = wiphy; request->scan_start = jiffies; ret = __wlan_hdd_cfg80211_scan(wiphy, request, VENDOR_SCAN); if (0 != ret) { hdd_err("Scan Failed. Ret = %d", ret); qdf_mem_free(request); return ret; } ret = wlan_hdd_send_scan_start_event(wiphy, wdev, (uintptr_t)request); return ret; error: hdd_err("Scan Request Failed"); qdf_mem_free(request); return -EINVAL; } /** * wlan_hdd_cfg80211_vendor_scan() -API to process venor scan request * @wiphy: Pointer to wiphy * @wdev: Pointer to wireless device * @data: Pointer to the data * @data_len: length of the data * * This is called from userspace to request scan. * * Return: Return the Success or Failure code. */ int wlan_hdd_cfg80211_vendor_scan(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int data_len) { int errno; struct osif_vdev_sync *vdev_sync; errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); if (errno) return errno; errno = __wlan_hdd_cfg80211_vendor_scan(wiphy, wdev, data, data_len); osif_vdev_sync_op_stop(vdev_sync); return errno; } /** * __wlan_hdd_vendor_abort_scan() - API to process vendor command for * abort scan * @wiphy: Pointer to wiphy * @data: Pointer to the data * @data_len: length of the data * * API to process vendor abort scan * * Return: zero for success and non zero for failure */ static int __wlan_hdd_vendor_abort_scan( struct wiphy *wiphy, const void *data, int data_len) { struct hdd_context *hdd_ctx = wiphy_priv(wiphy); int ret; if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { hdd_err("Command not allowed in FTM mode"); return -EINVAL; } ret = wlan_hdd_validate_context(hdd_ctx); if (0 != ret) return ret; wlan_vendor_abort_scan(hdd_ctx->pdev, data, data_len); return ret; } /** * wlan_hdd_vendor_abort_scan() - API to process vendor command for * abort scan * @wiphy: Pointer to wiphy * @wdev: Pointer to net device * @data : Pointer to the data * @data_len : length of the data * * This is called from supplicant to abort scan * * Return: zero for success and non zero for failure */ int wlan_hdd_vendor_abort_scan(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int data_len) { struct osif_vdev_sync *vdev_sync; int errno; errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); if (errno) return errno; errno = __wlan_hdd_vendor_abort_scan(wiphy, data, data_len); osif_vdev_sync_op_stop(vdev_sync); return errno; } int wlan_hdd_scan_abort(struct wlan_hdd_link_info *link_info) { struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(link_info->adapter); wlan_abort_scan(hdd_ctx->pdev, INVAL_PDEV_ID, link_info->vdev_id, INVALID_SCAN_ID, true); return 0; } #ifdef FEATURE_WLAN_SCAN_PNO /** * __wlan_hdd_cfg80211_sched_scan_start() - cfg80211 scheduled scan(pno) start * @wiphy: Pointer to wiphy * @dev: Pointer network device * @request: Pointer to cfg80211 scheduled scan start request * * Return: 0 for success, non zero for failure */ static int __wlan_hdd_cfg80211_sched_scan_start(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_sched_scan_request *request) { struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); struct hdd_context *hdd_ctx; struct wlan_objmgr_vdev *vdev; int ret; bool pno_offload_enabled; uint8_t scan_backoff_multiplier; bool enable_connected_scan; enum QDF_GLOBAL_MODE curr_mode; curr_mode = hdd_get_conparam(); if (QDF_GLOBAL_FTM_MODE == curr_mode || QDF_GLOBAL_MONITOR_MODE == curr_mode) { hdd_err_rl("Command not allowed in FTM/Monitor mode"); return -EINVAL; } if (wlan_hdd_validate_vdev_id(adapter->deflink->vdev_id)) return -EINVAL; if (adapter->device_mode != QDF_STA_MODE) { hdd_info("Sched scans only supported on STA ifaces"); return -EINVAL; } hdd_ctx = WLAN_HDD_GET_CTX(adapter); ret = wlan_hdd_validate_context(hdd_ctx); if (ret) return ret; pno_offload_enabled = ucfg_scan_is_pno_offload_enabled(hdd_ctx->psoc); if (!pno_offload_enabled) { hdd_debug("Pno Offload is not enabled"); return -EINVAL; } enable_connected_scan = ucfg_scan_is_connected_scan_enabled( hdd_ctx->psoc); if (!enable_connected_scan && hdd_cm_is_vdev_associated(adapter->deflink)) { hdd_info("enable_connected_scan is false, Aborting scan"); return -EBUSY; } vdev = hdd_objmgr_get_vdev_by_user(adapter->deflink, WLAN_OSIF_SCAN_ID); if (!vdev) return -EINVAL; scan_backoff_multiplier = ucfg_get_scan_backoff_multiplier(hdd_ctx->psoc); ret = wlan_cfg80211_sched_scan_start(vdev, request, scan_backoff_multiplier); hdd_objmgr_put_vdev_by_user(vdev, WLAN_OSIF_SCAN_ID); return ret; } /** * wlan_hdd_cfg80211_sched_scan_start() - cfg80211 scheduled scan(pno) start * @wiphy: Pointer to wiphy * @dev: Pointer network device * @request: Pointer to cfg80211 scheduled scan start request * * Return: 0 for success, non zero for failure */ int wlan_hdd_cfg80211_sched_scan_start(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_sched_scan_request *request) { int errno; struct osif_vdev_sync *vdev_sync; errno = osif_vdev_sync_op_start(dev, &vdev_sync); if (errno) return errno; errno = __wlan_hdd_cfg80211_sched_scan_start(wiphy, dev, request); osif_vdev_sync_op_stop(vdev_sync); return errno; } int wlan_hdd_sched_scan_stop(struct net_device *dev) { struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); struct hdd_context *hdd_ctx; struct wlan_objmgr_vdev *vdev; int ret; bool pno_offload_enabled; 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; hdd_ctx = WLAN_HDD_GET_CTX(adapter); if (!hdd_ctx) { hdd_err("HDD context is Null"); return -EINVAL; } pno_offload_enabled = ucfg_scan_is_pno_offload_enabled(hdd_ctx->psoc); if (!pno_offload_enabled) { hdd_debug("PnoOffload is not enabled!!!"); return -EINVAL; } vdev = hdd_objmgr_get_vdev_by_user(adapter->deflink, WLAN_OSIF_SCAN_ID); if (!vdev) return -EINVAL; ret = wlan_cfg80211_sched_scan_stop(vdev); hdd_objmgr_put_vdev_by_user(vdev, WLAN_OSIF_SCAN_ID); return ret; } /** * __wlan_hdd_cfg80211_sched_scan_stop() - stop cfg80211 scheduled scan(pno) * @dev: Pointer network device * * This is a wrapper around wlan_hdd_sched_scan_stop() that returns success * in the event that the driver is currently recovering or unloading. This * prevents a race condition where we get a scan stop from kernel during * a driver unload from PLD. * * Return: 0 for success, non zero for failure */ static int __wlan_hdd_cfg80211_sched_scan_stop(struct net_device *dev) { struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); int errno; enum QDF_GLOBAL_MODE curr_mode; curr_mode = hdd_get_conparam(); if (QDF_GLOBAL_FTM_MODE == curr_mode || QDF_GLOBAL_MONITOR_MODE == curr_mode) { hdd_err_rl("Command not allowed in FTM/Monitor mode"); return -EINVAL; } /* The return 0 is intentional when Recovery and Load/Unload in * progress. We did observe a crash due to a return of * failure in sched_scan_stop , especially for a case where the unload * of the happens at the same time. The function * __cfg80211_stop_sched_scan was clearing rdev->sched_scan_req only * when the sched_scan_stop returns success. If it returns a failure , * then its next invocation due to the clean up of the second interface * will have the dev pointer corresponding to the first one leading to * a crash. */ if (cds_is_driver_recovering() || cds_is_driver_in_bad_state()) { hdd_info("Recovery in Progress. State: 0x%x Ignore!!!", cds_get_driver_state()); return 0; } if (cds_is_load_or_unload_in_progress()) { hdd_info("Unload/Load in Progress, state: 0x%x. Ignore!!!", cds_get_driver_state()); return 0; } errno = hdd_validate_adapter(adapter); if (errno) return errno; if (adapter->device_mode != QDF_STA_MODE) { hdd_info("Sched scans only supported on STA ifaces"); return -EINVAL; } errno = wlan_hdd_validate_context(WLAN_HDD_GET_CTX(adapter)); if (errno) return errno; errno = wlan_hdd_sched_scan_stop(dev); return errno; } #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0) int wlan_hdd_cfg80211_sched_scan_stop(struct wiphy *wiphy, struct net_device *dev) { int errno; struct osif_vdev_sync *vdev_sync; errno = osif_vdev_sync_op_start(dev, &vdev_sync); if (errno) return errno; errno = __wlan_hdd_cfg80211_sched_scan_stop(dev); osif_vdev_sync_op_stop(vdev_sync); /* The return 0 is intentional. We observed a crash due to a return of * failure in sched_scan_stop , especially for a case where the unload * of the happens at the same time. The function * __cfg80211_stop_sched_scan was clearing rdev->sched_scan_req only * when the sched_scan_stop returns success. If it returns a failure , * then its next invocation due to the clean up of the second interface * will have the dev pointer corresponding to the first one leading to * a crash. */ return 0; } #else int wlan_hdd_cfg80211_sched_scan_stop(struct wiphy *wiphy, struct net_device *dev, uint64_t reqid) { int errno; struct osif_vdev_sync *vdev_sync; errno = osif_vdev_sync_op_start(dev, &vdev_sync); if (errno) return errno; errno = __wlan_hdd_cfg80211_sched_scan_stop(dev); osif_vdev_sync_op_stop(vdev_sync); /* The return 0 is intentional. We observed a crash due to a return of * failure in sched_scan_stop , especially for a case where the unload * of the happens at the same time. The function * __cfg80211_stop_sched_scan was clearing rdev->sched_scan_req only * when the sched_scan_stop returns success. If it returns a failure , * then its next invocation due to the clean up of the second interface * will have the dev pointer corresponding to the first one leading to * a crash. */ return 0; } #endif /* KERNEL_VERSION(4, 12, 0) */ #endif /*FEATURE_WLAN_SCAN_PNO */ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0)) || \ defined(CFG80211_ABORT_SCAN) /** * __wlan_hdd_cfg80211_abort_scan() - cfg80211 abort scan api * @wiphy: Pointer to wiphy * @wdev: Pointer to wireless device structure * * This function is used to abort an ongoing scan * * Return: None */ static void __wlan_hdd_cfg80211_abort_scan(struct wiphy *wiphy, struct wireless_dev *wdev) { struct net_device *dev = wdev->netdev; struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); struct hdd_context *hdd_ctx = wiphy_priv(wiphy); int ret; hdd_enter_dev(dev); if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { hdd_err("Command not allowed in FTM mode"); return; } if (wlan_hdd_validate_vdev_id(adapter->deflink->vdev_id)) return; ret = wlan_hdd_validate_context(hdd_ctx); if (ret) return; wlan_cfg80211_abort_scan(hdd_ctx->pdev); hdd_exit(); } /** * wlan_hdd_cfg80211_abort_scan - cfg80211 abort scan api * @wiphy: Pointer to wiphy * @wdev: Pointer to wireless device structure * * Wrapper to __wlan_hdd_cfg80211_abort_scan() - * function is used to abort an ongoing scan * * Return: None */ void wlan_hdd_cfg80211_abort_scan(struct wiphy *wiphy, struct wireless_dev *wdev) { struct osif_psoc_sync *psoc_sync; int errno; errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync); if (errno) return; __wlan_hdd_cfg80211_abort_scan(wiphy, wdev); osif_psoc_sync_op_stop(psoc_sync); } #endif /** * hdd_scan_context_destroy() - Destroy scan context * @hdd_ctx: HDD context. * * Destroy scan context. * * Return: None. */ void hdd_scan_context_destroy(struct hdd_context *hdd_ctx) { } /** * hdd_scan_context_init() - Initialize scan context * @hdd_ctx: HDD context. * * Initialize scan related resources like spin lock and lists. * * Return: 0 on success and errno on failure. */ int hdd_scan_context_init(struct hdd_context *hdd_ctx) { return 0; }