/****************************************************************************** * * Copyright(c) 2019 - 2020 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * *****************************************************************************/ #include /*rtw_mlme.c*/ void rtw_init_sitesurvey_parm(_adapter *padapter, struct sitesurvey_parm *pparm) { struct mlme_priv *pmlmepriv = &padapter->mlmepriv; _rtw_memset(pparm, 0, sizeof(struct sitesurvey_parm)); pparm->scan_mode = pmlmepriv->scan_mode; } #ifdef CONFIG_SET_SCAN_DENY_TIMER inline bool rtw_is_scan_deny(_adapter *adapter) { struct mlme_priv *mlmepriv = &adapter->mlmepriv; return (ATOMIC_READ(&mlmepriv->set_scan_deny) != 0) ? _TRUE : _FALSE; } inline void rtw_clear_scan_deny(_adapter *adapter) { struct mlme_priv *mlmepriv = &adapter->mlmepriv; ATOMIC_SET(&mlmepriv->set_scan_deny, 0); if (0) RTW_INFO(FUNC_ADPT_FMT"\n", FUNC_ADPT_ARG(adapter)); } void rtw_set_scan_deny_timer_hdl(void *ctx) { _adapter *adapter = (_adapter *)ctx; rtw_clear_scan_deny(adapter); } void rtw_set_scan_deny(_adapter *adapter, u32 ms) { struct mlme_priv *mlmepriv = &adapter->mlmepriv; if (0) RTW_INFO(FUNC_ADPT_FMT"\n", FUNC_ADPT_ARG(adapter)); ATOMIC_SET(&mlmepriv->set_scan_deny, 1); _set_timer(&mlmepriv->set_scan_deny_timer, ms); } #endif void rtw_drv_scan_by_self(_adapter *padapter, u8 reason) { struct sitesurvey_parm parm; struct mlme_priv *pmlmepriv = &padapter->mlmepriv; int i; #if 1 u8 ssc_chk; ssc_chk = rtw_sitesurvey_condition_check(padapter, _FALSE); if( ssc_chk == SS_DENY_BUSY_TRAFFIC) { #ifdef CONFIG_LAYER2_ROAMING if (rtw_chk_roam_flags(padapter, RTW_ROAM_ACTIVE) && pmlmepriv->need_to_roam == _TRUE) { RTW_INFO(FUNC_ADPT_FMT" need to roam, don't care BusyTraffic\n", FUNC_ADPT_ARG(padapter)); } else #endif { RTW_INFO(FUNC_ADPT_FMT" exit BusyTraffic\n", FUNC_ADPT_ARG(padapter)); goto exit; } } else if (ssc_chk != SS_ALLOW) { goto exit; } if (!rtw_is_adapter_up(padapter)) goto exit; #else if (rtw_is_scan_deny(padapter)) goto exit; if (!rtw_is_adapter_up(padapter)) goto exit; if (rtw_mi_busy_traffic_check(padapter)) { #ifdef CONFIG_LAYER2_ROAMING if (rtw_chk_roam_flags(padapter, RTW_ROAM_ACTIVE) && pmlmepriv->need_to_roam == _TRUE) { RTW_INFO("need to roam, don't care BusyTraffic\n"); } else #endif { RTW_INFO(FUNC_ADPT_FMT" exit BusyTraffic\n", FUNC_ADPT_ARG(padapter)); goto exit; } } if (check_fwstate(pmlmepriv, WIFI_AP_STATE) && check_fwstate(pmlmepriv, WIFI_UNDER_WPS)) { RTW_INFO(FUNC_ADPT_FMT" WIFI_AP_STATE && WIFI_UNDER_WPS\n", FUNC_ADPT_ARG(padapter)); goto exit; } if (check_fwstate(pmlmepriv, (WIFI_UNDER_SURVEY | WIFI_UNDER_LINKING)) == _TRUE) { RTW_INFO(FUNC_ADPT_FMT" WIFI_UNDER_SURVEY|WIFI_UNDER_LINKING\n", FUNC_ADPT_ARG(padapter)); goto exit; } #ifdef CONFIG_CONCURRENT_MODE if (rtw_mi_buddy_check_fwstate(padapter, (WIFI_UNDER_SURVEY | WIFI_UNDER_LINKING | WIFI_UNDER_WPS))) { RTW_INFO(FUNC_ADPT_FMT", but buddy_intf is under scanning or linking or wps_phase\n", FUNC_ADPT_ARG(padapter)); goto exit; } #endif #endif RTW_INFO(FUNC_ADPT_FMT" reason:0x%02x\n", FUNC_ADPT_ARG(padapter), reason); /* only for 20/40 BSS */ if (reason == RTW_AUTO_SCAN_REASON_2040_BSS) { rtw_init_sitesurvey_parm(padapter, &parm); for (i=0;i<14;i++) { parm.ch[i].hw_value = i + 1; parm.ch[i].flags = RTW_IEEE80211_CHAN_PASSIVE_SCAN; } parm.ch_num = 14; rtw_sitesurvey_cmd(padapter, &parm); goto exit; } #ifdef CONFIG_RTW_MBO #if defined(CONFIG_RTW_WNM) || defined(CONFIG_RTW_80211K) if ((reason == RTW_AUTO_SCAN_REASON_ROAM) && (rtw_roam_nb_scan_list_set(padapter, &parm))) goto exit; #endif #endif rtw_sitesurvey_cmd(padapter, NULL); exit: return; } #ifdef CONFIG_RTW_ACS u8 rtw_set_acs_sitesurvey(_adapter *adapter) { struct rf_ctl_t *rfctl = adapter_to_rfctl(adapter); struct sitesurvey_parm parm; u8 uch; u8 ch_num = 0; int i; enum band_type band; u8 (*center_chs_num)(u8) = NULL; u8 (*center_chs)(u8, u8) = NULL; u8 ret = _FAIL; if (!rtw_mi_get_ch_setting_union(adapter, &uch, NULL, NULL)) goto exit; _rtw_memset(&parm, 0, sizeof(struct sitesurvey_parm)); parm.scan_mode = RTW_PHL_SCAN_PASSIVE; parm.bw = CHANNEL_WIDTH_20; parm.acs = 1; for (band = BAND_ON_24G; band < BAND_MAX; band++) { if (band == BAND_ON_24G) { center_chs_num = center_chs_2g_num; center_chs = center_chs_2g; } else #if CONFIG_IEEE80211_BAND_5GHZ if (band == BAND_ON_5G) { center_chs_num = center_chs_5g_num; center_chs = center_chs_5g; } else #endif { center_chs_num = NULL; center_chs = NULL; } if (!center_chs_num || !center_chs) continue; if (rfctl->ch_sel_within_same_band) { if (rtw_is_2g_ch(uch) && band != BAND_ON_24G) continue; #if CONFIG_IEEE80211_BAND_5GHZ if (rtw_is_5g_ch(uch) && band != BAND_ON_5G) continue; #endif } ch_num = center_chs_num(CHANNEL_WIDTH_20); for (i = 0; i < ch_num && parm.ch_num < RTW_CHANNEL_SCAN_AMOUNT; i++) { parm.ch[parm.ch_num].hw_value = center_chs(CHANNEL_WIDTH_20, i); parm.ch[parm.ch_num].flags = RTW_IEEE80211_CHAN_PASSIVE_SCAN; parm.ch_num++; } } ret = rtw_sitesurvey_cmd(adapter, &parm); exit: return ret; } #endif /* CONFIG_RTW_ACS */ static u32 _rtw_wait_scan_done(_adapter *adapter, u32 timeout_ms) { systime start; u32 pass_ms; struct mlme_priv *pmlmepriv = &(adapter->mlmepriv); struct mlme_ext_priv *pmlmeext = &(adapter->mlmeextpriv); u8 abort_timeout = false; start = rtw_get_current_time(); while ((rtw_cfg80211_get_is_roch(adapter) == _TRUE || check_fwstate(pmlmepriv, WIFI_UNDER_SURVEY)) && rtw_get_passing_time_ms(start) <= timeout_ms) { if (RTW_CANNOT_RUN(adapter_to_dvobj(adapter))) break; RTW_INFO(FUNC_NDEV_FMT"fw_state=WIFI_UNDER_SURVEY!\n", FUNC_NDEV_ARG(adapter->pnetdev)); rtw_msleep_os(20); abort_timeout = true; } if (_TRUE == abort_timeout && check_fwstate(pmlmepriv, WIFI_UNDER_SURVEY)) { if (!RTW_CANNOT_RUN(adapter_to_dvobj(adapter))) RTW_ERR(FUNC_NDEV_FMT"waiting for scan_abort time out!\n", FUNC_NDEV_ARG(adapter->pnetdev)); pmlmeext->scan_abort_to = _TRUE; #ifdef CONFIG_PLATFORM_MSTAR /*_clr_fwstate_(pmlmepriv, WIFI_UNDER_SURVEY);*/ /*set_survey_timer(pmlmeext, 0);*/ mlme_set_scan_to_timer(pmlmepriv, 50); #endif rtw_indicate_scan_done(adapter, _TRUE); } pmlmeext->scan_abort = _FALSE; RTW_INFO(FUNC_ADPT_FMT "- %s....scan_abort:%d\n", FUNC_ADPT_ARG(adapter), __func__, pmlmeext->scan_abort); pass_ms = rtw_get_passing_time_ms(start); RTW_INFO("%s scan timeout value:%d ms, total take:%d ms\n", __func__, timeout_ms, pass_ms); return pass_ms; } /* * timeout_ms > 0:rtw_scan_abort_timeout , = 0:rtw_scan_wait_completed */ u32 rtw_scan_abort(_adapter *adapter, u32 timeout_ms) { struct mlme_priv *pmlmepriv = &(adapter->mlmepriv); struct mlme_ext_priv *pmlmeext = &adapter->mlmeextpriv; enum rtw_phl_status psts = RTW_PHL_STATUS_FAILURE; u32 pass_ms = 0; if (rtw_cfg80211_get_is_roch(adapter) == _TRUE || check_fwstate(pmlmepriv, WIFI_UNDER_SURVEY)) { pmlmeext->scan_abort = _TRUE; RTW_INFO(FUNC_ADPT_FMT "- %s....scan_abort:%d\n", FUNC_ADPT_ARG(adapter), __func__, pmlmeext->scan_abort); rtw_sctx_init(&pmlmeext->sitesurvey_res.sctx, timeout_ms); #ifdef CONFIG_CMD_SCAN if (pmlmeext->sitesurvey_res.scan_param) psts = rtw_phl_cmd_scan_cancel(adapter_to_dvobj(adapter)->phl, pmlmeext->sitesurvey_res.scan_param); #else psts = rtw_phl_scan_cancel(adapter_to_dvobj(adapter)->phl); #endif if (psts == RTW_PHL_STATUS_SUCCESS) rtw_sctx_wait(&pmlmeext->sitesurvey_res.sctx, __func__); pass_ms = _rtw_wait_scan_done(adapter, timeout_ms); } return pass_ms; } void rtw_scan_abort_no_wait(_adapter *adapter) { struct mlme_ext_priv *pmlmeext = &adapter->mlmeextpriv; #ifdef CONFIG_CMD_SCAN if (pmlmeext->sitesurvey_res.scan_param) rtw_phl_cmd_scan_cancel(adapter_to_dvobj(adapter)->phl, pmlmeext->sitesurvey_res.scan_param); #else rtw_phl_scan_cancel(adapter_to_dvobj(adapter)->phl); #endif } /* * rtw_scan_timeout_handler - Timeout/Faliure handler for CMD SiteSurvey * @adapter: pointer to _adapter structure */ void rtw_scan_timeout_handler(void *ctx) { _adapter *adapter = (_adapter *)ctx; struct mlme_priv *pmlmepriv = &adapter->mlmepriv; RTW_INFO(FUNC_ADPT_FMT" fw_state=%x\n", FUNC_ADPT_ARG(adapter), get_fwstate(pmlmepriv)); rtw_scan_abort_no_wait(adapter); #if 0 _rtw_spinlock_bh(&pmlmepriv->lock); _clr_fwstate_(pmlmepriv, WIFI_UNDER_SURVEY); _rtw_spinunlock_bh(&pmlmepriv->lock); #ifdef CONFIG_IOCTL_CFG80211 rtw_cfg80211_surveydone_event_callback(adapter); #endif /* CONFIG_IOCTL_CFG80211 */ rtw_indicate_scan_done(adapter, _TRUE); #endif } static inline bool _rtw_scan_abort_check(_adapter *adapter, const char *caller) { struct mlme_priv *pmlmepriv = &adapter->mlmepriv; struct mlme_ext_priv *pmlmeext = &adapter->mlmeextpriv; struct submit_ctx *sctx = &pmlmeext->sitesurvey_res.sctx; RTW_INFO(FUNC_ADPT_FMT "- %s....scan_abort:%d\n", FUNC_ADPT_ARG(adapter), __func__, pmlmeext->scan_abort); if (pmlmeext->scan_abort == _FALSE) return _FALSE; if (pmlmeext->scan_abort_to) { RTW_ERR("%s scan abort timeout\n", caller); rtw_warn_on(1); } _cancel_timer_ex(&pmlmepriv->scan_to_timer); pmlmeext->scan_abort = _FALSE; pmlmeext->scan_abort_to = _FALSE; if (sctx) { RTW_INFO("%s scan abort .....(%d ms)\n", caller, rtw_get_passing_time_ms(sctx->submit_time)); rtw_sctx_done(&sctx); } return _TRUE; } static struct wlan_network *alloc_network(struct mlme_priv *pmlmepriv) /* (_queue *free_queue) */ { struct wlan_network *pnetwork; pnetwork = _rtw_alloc_network(pmlmepriv); return pnetwork; } static void update_current_network(_adapter *adapter, WLAN_BSSID_EX *pnetwork) { struct mlme_priv *pmlmepriv = &(adapter->mlmepriv); if ((check_fwstate(pmlmepriv, WIFI_ASOC_STATE) == _TRUE) && (is_same_network(&(pmlmepriv->cur_network.network), pnetwork))) { /* if(pmlmepriv->cur_network.network.IELength<= pnetwork->IELength) */ { rtw_update_network(&(pmlmepriv->cur_network.network), pnetwork, adapter, _TRUE); rtw_update_protection(adapter, (pmlmepriv->cur_network.network.IEs) + sizeof(NDIS_802_11_FIXED_IEs), pmlmepriv->cur_network.network.IELength); } } } /*Caller must hold pmlmepriv->lock first.*/ static bool update_scanned_network(_adapter *adapter, WLAN_BSSID_EX *target) { _list *plist, *phead; u32 bssid_ex_sz; struct mlme_priv *pmlmepriv = &(adapter->mlmepriv); #ifdef CONFIG_P2P struct wifidirect_info *pwdinfo = &(adapter->wdinfo); #endif /* CONFIG_P2P */ _queue *queue = &(pmlmepriv->scanned_queue); struct wlan_network *pnetwork = NULL; struct wlan_network *choice = NULL; int target_find = 0; bool update_ie = _FALSE; _rtw_spinlock_bh(&queue->lock); phead = get_list_head(queue); plist = get_next(phead); #if 0 RTW_INFO("%s => ssid:%s , rssi:%ld , ss:%d\n", __func__, target->Ssid.Ssid, target->PhyInfo.rssi, target->PhyInfo.SignalStrength); #endif while (1) { if (rtw_end_of_queue_search(phead, plist) == _TRUE) break; pnetwork = LIST_CONTAINOR(plist, struct wlan_network, list); #ifdef CONFIG_P2P if (_rtw_memcmp(pnetwork->network.MacAddress, target->MacAddress, ETH_ALEN) && _rtw_memcmp(pnetwork->network.Ssid.Ssid, "DIRECT-", 7) && rtw_get_p2p_ie(pnetwork->network.IEs + _FIXED_IE_LENGTH_, pnetwork->network.IELength - _FIXED_IE_LENGTH_, NULL, NULL)) { target_find = 1; break; } #endif if (is_same_network(&(pnetwork->network), target)) { target_find = 1; break; } if (rtw_roam_flags(adapter)) { /* TODO: don't select netowrk in the same ess as choice if it's new enough*/ } if (pnetwork->fixed) { plist = get_next(plist); continue; } #ifdef CONFIG_RSSI_PRIORITY if ((choice == NULL) || (pnetwork->network.PhyInfo.SignalStrength < choice->network.PhyInfo.SignalStrength)) #ifdef CONFIG_RTW_MESH if (!MLME_IS_MESH(adapter) || !MLME_IS_ASOC(adapter) || !rtw_bss_is_same_mbss(&pmlmepriv->cur_network.network, &pnetwork->network)) #endif choice = pnetwork; #else if (choice == NULL || rtw_time_after(choice->last_scanned, pnetwork->last_scanned)) #ifdef CONFIG_RTW_MESH if (!MLME_IS_MESH(adapter) || !MLME_IS_ASOC(adapter) || !rtw_bss_is_same_mbss(&pmlmepriv->cur_network.network, &pnetwork->network)) #endif choice = pnetwork; #endif plist = get_next(plist); } /* If we didn't find a match, then get a new network slot to initialize * with this beacon's information */ /* if (rtw_end_of_queue_search(phead,plist)== _TRUE) { */ if (!target_find) { if (_rtw_queue_empty(&(pmlmepriv->free_bss_pool)) == _TRUE) { /* If there are no more slots, expire the choice */ /* list_del_init(&choice->list); */ pnetwork = choice; if (pnetwork == NULL) goto unlock_scan_queue; #ifdef CONFIG_RSSI_PRIORITY RTW_DBG("%s => ssid:%s ,bssid:"MAC_FMT" will be deleted from scanned_queue (rssi:%d , ss:%d)\n", __func__, pnetwork->network.Ssid.Ssid, MAC_ARG(pnetwork->network.MacAddress), pnetwork->network.PhyInfo.rssi, pnetwork->network.PhyInfo.SignalStrength); #else RTW_DBG("%s => ssid:%s ,bssid:"MAC_FMT" will be deleted from scanned_queue\n", __func__, pnetwork->network.Ssid.Ssid, MAC_ARG(pnetwork->network.MacAddress)); #endif _rtw_memcpy(&(pnetwork->network), target, get_WLAN_BSSID_EX_sz(target)); pnetwork->bcn_keys_valid = 0; if (target->Reserved[0] == BSS_TYPE_BCN || target->Reserved[0] == BSS_TYPE_PROB_RSP) rtw_update_bcn_keys_of_network(pnetwork); /* variable initialize */ pnetwork->fixed = _FALSE; pnetwork->last_scanned = rtw_get_current_time(); pnetwork->last_non_hidden_ssid_ap = pnetwork->last_scanned; #if defined(CONFIG_RTW_MESH) && CONFIG_RTW_MESH_ACNODE_PREVENT pnetwork->acnode_stime = 0; pnetwork->acnode_notify_etime = 0; #endif pnetwork->network_type = 0; pnetwork->aid = 0; pnetwork->join_res = 0; /* bss info not receving from the right channel */ if (pnetwork->network.PhyInfo.SignalQuality == 101) pnetwork->network.PhyInfo.SignalQuality = 0; } else { /* Otherwise just pull from the free list */ pnetwork = alloc_network(pmlmepriv); /* will update scan_time */ if (pnetwork == NULL) goto unlock_scan_queue; bssid_ex_sz = get_WLAN_BSSID_EX_sz(target); target->Length = bssid_ex_sz; _rtw_memcpy(&(pnetwork->network), target, bssid_ex_sz); pnetwork->bcn_keys_valid = 0; if (target->Reserved[0] == BSS_TYPE_BCN || target->Reserved[0] == BSS_TYPE_PROB_RSP) rtw_update_bcn_keys_of_network(pnetwork); /* bss info not receving from the right channel */ if (pnetwork->network.PhyInfo.SignalQuality == 101) pnetwork->network.PhyInfo.SignalQuality = 0; rtw_list_insert_tail(&(pnetwork->list), &(queue->queue)); } } else { /* we have an entry and we are going to update it. But this entry may * be already expired. In this case we do the same as we found a new * net and call the new_net handler */ #if defined(CONFIG_RTW_MESH) && CONFIG_RTW_MESH_ACNODE_PREVENT systime last_scanned = pnetwork->last_scanned; #endif struct beacon_keys bcn_keys; bool bcn_keys_valid = 0; bool is_hidden_ssid_ap = 0; pnetwork->last_scanned = rtw_get_current_time(); if (target->Reserved[0] == BSS_TYPE_BCN || target->Reserved[0] == BSS_TYPE_PROB_RSP) { if (target->InfrastructureMode == Ndis802_11Infrastructure) { is_hidden_ssid_ap = hidden_ssid_ap(target); if (!is_hidden_ssid_ap) /* update last time it's non hidden ssid AP */ pnetwork->last_non_hidden_ssid_ap = rtw_get_current_time(); } bcn_keys_valid = rtw_get_bcn_keys_from_bss(target, &bcn_keys); } if (target->InfrastructureMode == Ndis802_11_mesh || target->Reserved[0] >= pnetwork->network.Reserved[0]) update_ie = _TRUE; else if (target->InfrastructureMode == Ndis802_11Infrastructure && !pnetwork->fixed && rtw_get_passing_time_ms(pnetwork->last_non_hidden_ssid_ap) > SCANQUEUE_LIFETIME) update_ie = _TRUE; else if (bcn_keys_valid) { if (is_hidden_ssid(bcn_keys.ssid, bcn_keys.ssid_len)) { /* hidden ssid, replace with current beacon ssid directly */ _rtw_memcpy(bcn_keys.ssid, pnetwork->bcn_keys.ssid, pnetwork->bcn_keys.ssid_len); bcn_keys.ssid_len = pnetwork->bcn_keys.ssid_len; } if (rtw_bcn_key_compare(&pnetwork->bcn_keys, &bcn_keys) == _FALSE) update_ie = _TRUE; } #if defined(CONFIG_RTW_MESH) && CONFIG_RTW_MESH_ACNODE_PREVENT if (!MLME_IS_MESH(adapter) || !MLME_IS_ASOC(adapter) || pnetwork->network.Configuration.DSConfig != target->Configuration.DSConfig || rtw_get_passing_time_ms(last_scanned) > adapter->mesh_cfg.peer_sel_policy.scanr_exp_ms || !rtw_bss_is_same_mbss(&pnetwork->network, target) ) { pnetwork->acnode_stime = 0; pnetwork->acnode_notify_etime = 0; } #endif if (bcn_keys_valid) { _rtw_memcpy(&pnetwork->bcn_keys, &bcn_keys, sizeof(bcn_keys)); pnetwork->bcn_keys_valid = 1; } else if (update_ie) pnetwork->bcn_keys_valid = 0; rtw_update_network(&(pnetwork->network), target, adapter, update_ie); } #if defined(CONFIG_RTW_MESH) && CONFIG_RTW_MESH_ACNODE_PREVENT if (MLME_IS_MESH(adapter) && MLME_IS_ASOC(adapter)) rtw_mesh_update_scanned_acnode_status(adapter, pnetwork); #endif unlock_scan_queue: _rtw_spinunlock_bh(&queue->lock); #ifdef CONFIG_RTW_MESH if (pnetwork && MLME_IS_MESH(adapter) && check_fwstate(pmlmepriv, WIFI_ASOC_STATE) && !check_fwstate(pmlmepriv, WIFI_UNDER_SURVEY) ) rtw_chk_candidate_peer_notify(adapter, pnetwork); #endif return update_ie; } static void add_network(_adapter *adapter, WLAN_BSSID_EX *pnetwork) { bool update_ie; /* _queue *queue = &(pmlmepriv->scanned_queue); */ /* _rtw_spinlock_bh(&queue->lock); */ #if defined(CONFIG_P2P) && defined(CONFIG_P2P_REMOVE_GROUP_INFO) if (adapter->registrypriv.wifi_spec == 0) rtw_bss_ex_del_p2p_attr(pnetwork, P2P_ATTR_GROUP_INFO); #endif #ifdef CONFIG_IGNORE_GO_AND_LOW_RSSI_IN_SCAN_LIST if (adapter->registrypriv.ignore_go_in_scan) { if(rtw_chk_p2p_wildcard_ssid(pnetwork) == _SUCCESS || rtw_chk_p2p_ie(pnetwork) == _SUCCESS) return; } /*100 was follow n & ac IC setting SignalStrength rang was 0~100*/ if(adapter->registrypriv->ignore_low_rssi_in_scan != 0xff && pnetwork->PhyInfo.rssi < (adapter->registrypriv->ignore_low_rssi_in_scan - 100)) return; #endif /*CONFIG_IGNORE_GO_AND_LOW_RSSI_IN_SCAN_LIST*/ if (!rtw_hw_chk_wl_func(adapter_to_dvobj(adapter), WL_FUNC_MIRACAST)) rtw_bss_ex_del_wfd_ie(pnetwork); /* Wi-Fi driver will update the current network if the scan result of the connected AP be updated by scan. */ update_ie = update_scanned_network(adapter, pnetwork); if (update_ie) update_current_network(adapter, pnetwork); /* _rtw_spinunlock_bh(&queue->lock); */ } #ifdef CONFIG_STA_MULTIPLE_BSSID static inline void rtw_gen_new_bssid(const u8 *bssid, u8 max_bssid_ind, u8 mbssid_index, u8 *new_bssid) { u8 i = 0; u8 max_num = 1; u8 B; u8 new_a5; for (i = 0; i < max_bssid_ind; i++) max_num = max_num * 2; /*RTW_INFO("%s, max_num=%d\n", __func__, max_num);*/ /*RTW_INFO("%s, %02x,%02x,%02x,%02x,%02x,%02x \n", __func__, bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5]);*/ B = bssid[5] % max_num; new_a5 = bssid[5] - B + ((B + mbssid_index) % max_num); new_bssid[0] = bssid[0]; new_bssid[1] = bssid[1]; new_bssid[2] = bssid[2]; new_bssid[3] = bssid[3]; new_bssid[4] = bssid[4]; new_bssid[5] = new_a5; /*RTW_INFO("%s, %02x,%02x,%02x,%02x,%02x,%02x \n", __func__, new_bssid[0], new_bssid[1], new_bssid[2], new_bssid[3], new_bssid[4], new_bssid[5]);*/ } void add_mbssid_network(_adapter *padapter, WLAN_BSSID_EX *ref_bss) { WLAN_BSSID_EX *pbss; u32 sub_ies_len; u8 *mbssid_ie_ptr = NULL; PNDIS_802_11_VARIABLE_IEs pIE, sub_pie; u8 max_bssid_indicator; int i,j; u8* mbssid_ie; sint mbssid_len; u8 mbssid_index; u8 copy_ie_offset; u32 copy_ie_len = 0; mbssid_ie = rtw_get_ie(ref_bss->IEs + _BEACON_IE_OFFSET_ , WLAN_EID_MULTIPLE_BSSID , &mbssid_len , (ref_bss->IELength- _BEACON_IE_OFFSET_)); if (!mbssid_ie) return; #if 0 else RTW_PRINT_DUMP("mbssid_ie: ", (const u8 *)mbssid_ie, mbssid_len); #endif mbssid_ie_ptr = mbssid_ie; max_bssid_indicator = GET_MBSSID_MAX_BSSID_INDOCATOR(mbssid_ie_ptr); /*RTW_INFO("%s, max_bssid_indicator=%d\n", __func__, max_bssid_indicator);*/ mbssid_ie_ptr = mbssid_ie_ptr + MBSSID_MAX_BSSID_INDICATOR_OFFSET; for (i = 0; i + 1 < mbssid_len;) { pIE = (PNDIS_802_11_VARIABLE_IEs)(mbssid_ie_ptr + i); switch (pIE->ElementID) { case MBSSID_NONTRANSMITTED_BSSID_PROFILE_ID: sub_ies_len = pIE->Length; pbss = (WLAN_BSSID_EX *)rtw_zmalloc(sizeof(WLAN_BSSID_EX)); if (pbss) { _rtw_memcpy(pbss, ref_bss, sizeof(WLAN_BSSID_EX)); _rtw_memset(pbss->IEs, 0, MAX_IE_SZ); copy_ie_len = _TIMESTAMP_ + _BEACON_ITERVAL_; _rtw_memcpy(pbss->IEs, ref_bss->IEs, copy_ie_len); } else { return; } for (j = 0; j + 1 < sub_ies_len;) { sub_pie = (PNDIS_802_11_VARIABLE_IEs)(pIE->data + j); switch (sub_pie->ElementID) { case WLAN_EID_NON_TX_BSSID_CAP: /*RTW_INFO("%s, sub_pie->Length=%d\n", __func__, sub_pie->Length);*/ /*RTW_PRINT_DUMP("WLAN_EID_NON_TX_BSSID_CAP: ", (const u8 *)sub_pie->data, sub_pie->Length);*/ copy_ie_offset = _TIMESTAMP_ + _BEACON_ITERVAL_; _rtw_memcpy(pbss->IEs + copy_ie_offset, sub_pie->data, sub_pie->Length); break; case WLAN_EID_SSID: /*RTW_PRINT_DUMP("WLAN_EID_SSID: ", (const u8 *)sub_pie->data, sub_pie->Length);*/ /*RTW_INFO("%s, ref_bss->IELength=%d\n", __func__, ref_bss->IELength);*/ /*RTW_PRINT_DUMP("A ref_bss->IEs: ", (const u8 *)ref_bss->IEs, ref_bss->IELength);*/ copy_ie_offset = _TIMESTAMP_ + _BEACON_ITERVAL_ + _CAPABILITY_; copy_ie_len = WLAN_IE_ID_LEN + WLAN_IE_LEN_LEN; _rtw_memcpy(pbss->IEs + copy_ie_offset, sub_pie, copy_ie_len); copy_ie_offset = copy_ie_offset + WLAN_IE_ID_LEN + WLAN_IE_LEN_LEN; _rtw_memcpy(pbss->IEs + copy_ie_offset, sub_pie->data, sub_pie->Length); _rtw_memcpy(pbss->IEs + copy_ie_offset + sub_pie->Length , ref_bss->IEs + copy_ie_offset + ref_bss->Ssid.SsidLength , ref_bss->IELength - (copy_ie_offset + ref_bss->Ssid.SsidLength)); pbss->IELength = ref_bss->IELength + (sub_pie->Length - ref_bss->Ssid.SsidLength); /*RTW_INFO("%s, ref_bss->Ssid.SsidLength=%d\n", __func__, ref_bss->Ssid.SsidLength);*/ /*RTW_INFO("%s, sub_pie->Length=%d\n", __func__, sub_pie->Length);*/ /*RTW_INFO("%s, pbss->IELength=%d\n", __func__, pbss->IELength);*/ /*RTW_PRINT_DUMP("B pbss->IEs: ", (const u8 *)pbss->IEs, pbss->IELength);*/ _rtw_memset(pbss->Ssid.Ssid, 0, pbss->Ssid.SsidLength); _rtw_memcpy(pbss->Ssid.Ssid, sub_pie->data, sub_pie->Length); pbss->Ssid.SsidLength = sub_pie->Length; break; case WLAN_EID_MULTI_BSSID_IDX: /*RTW_INFO("%s, sub_pie->Length=%d\n", __func__, sub_pie->Length);*/ /*RTW_PRINT_DUMP("WLAN_EID_MULTI_BSSID_IDX: ", (const u8 *)sub_pie->data, sub_pie->Length);*/ _rtw_memcpy(&mbssid_index, sub_pie->data, sub_pie->Length); /*RTW_INFO("%s,mbssid_index=%d\n", __func__, mbssid_index);*/ rtw_gen_new_bssid(ref_bss->MacAddress, max_bssid_indicator , mbssid_index, pbss->MacAddress); pbss->mbssid_index = mbssid_index; break; default: break; } j += (sub_pie->Length + WLAN_IE_ID_LEN + WLAN_IE_LEN_LEN); /*RTW_INFO("%s, j=%d\n", __func__, j);*/ } pbss->is_mbssid = _TRUE; add_network(padapter, pbss); rtw_mfree((u8 *)pbss, sizeof(WLAN_BSSID_EX)); break; case MBSSID_VENDOR_SPECIFIC_ID: break; default: break; } i += (pIE->Length + WLAN_IE_ID_LEN + WLAN_IE_LEN_LEN); /*RTW_INFO("%s, i=%d\n", __func__, i);*/ } } #endif void rtw_survey_event_callback(_adapter *adapter, u8 *pbuf) { u32 len; u8 val8; WLAN_BSSID_EX *pnetwork; struct mlme_priv *pmlmepriv = &(adapter->mlmepriv); pnetwork = (WLAN_BSSID_EX *)pbuf; len = get_WLAN_BSSID_EX_sz(pnetwork); if (len > (sizeof(WLAN_BSSID_EX))) { return; } #ifdef CONFIG_RTW_80211K val8 = 0; rtw_hal_get_hwreg(adapter, HW_VAR_FREECNT, &val8); /* use TSF if no free run counter */ if (val8==0) pnetwork->PhyInfo.free_cnt = (u32)rtw_hal_get_tsftr_by_port( adapter, rtw_hal_get_port(adapter)); #endif if (pnetwork->InfrastructureMode == Ndis802_11Infrastructure) { if (MLME_IS_SCAN(adapter)) { adapter->mlmeextpriv.sitesurvey_res.activate_ch_cnt += rtw_process_beacon_hint(adapter, pnetwork); } } _rtw_spinlock_bh(&pmlmepriv->lock); /* update IBSS_network 's timestamp */ if ((check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE)) == _TRUE) { if (_rtw_memcmp(&(pmlmepriv->cur_network.network.MacAddress), pnetwork->MacAddress, ETH_ALEN)) { struct wlan_network *ibss_wlan = NULL; _rtw_memcpy(pmlmepriv->cur_network.network.IEs, pnetwork->IEs, 8); _rtw_spinlock_bh(&(pmlmepriv->scanned_queue.lock)); ibss_wlan = _rtw_find_network(&pmlmepriv->scanned_queue, pnetwork->MacAddress); if (ibss_wlan) { _rtw_memcpy(ibss_wlan->network.IEs , pnetwork->IEs, 8); _rtw_spinunlock_bh(&(pmlmepriv->scanned_queue.lock)); goto exit; } _rtw_spinunlock_bh(&(pmlmepriv->scanned_queue.lock)); } } /* lock pmlmepriv->lock when you accessing network_q */ if ((check_fwstate(pmlmepriv, WIFI_UNDER_LINKING)) == _FALSE) { if (pnetwork->Ssid.Ssid[0] == 0) pnetwork->Ssid.SsidLength = 0; add_network(adapter, pnetwork); #ifdef CONFIG_STA_MULTIPLE_BSSID add_mbssid_network(adapter, pnetwork); #endif } exit: _rtw_spinunlock_bh(&pmlmepriv->lock); return; } void rtw_surveydone_event_callback(_adapter *adapter, u8 *pbuf) { struct surveydone_event *parm = (struct surveydone_event *)pbuf; struct mlme_priv *pmlmepriv = &adapter->mlmepriv; struct mlme_ext_priv *pmlmeext = &adapter->mlmeextpriv; struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info; _rtw_spinlock_bh(&pmlmepriv->lock); if (pmlmepriv->wps_probe_req_ie) { u32 free_len = pmlmepriv->wps_probe_req_ie_len; pmlmepriv->wps_probe_req_ie_len = 0; rtw_mfree(pmlmepriv->wps_probe_req_ie, free_len); pmlmepriv->wps_probe_req_ie = NULL; } if (check_fwstate(pmlmepriv, WIFI_UNDER_SURVEY) == _FALSE) { RTW_INFO(FUNC_ADPT_FMT" fw_state:0x%x\n", FUNC_ADPT_ARG(adapter), get_fwstate(pmlmepriv)); /* rtw_warn_on(1); */ } _clr_fwstate_(pmlmepriv, WIFI_UNDER_SURVEY); _rtw_spinunlock_bh(&pmlmepriv->lock); _cancel_timer_ex(&pmlmepriv->scan_to_timer); _rtw_spinlock_bh(&pmlmepriv->lock); #ifdef CONFIG_SIGNAL_STAT_PROCESS rtw_set_signal_stat_timer(&adapter->recvinfo); #endif if (pmlmepriv->to_join == _TRUE) { if ((check_fwstate(pmlmepriv, WIFI_ADHOC_STATE) == _TRUE)) { if (check_fwstate(pmlmepriv, WIFI_ASOC_STATE) == _FALSE) { set_fwstate(pmlmepriv, WIFI_UNDER_LINKING); if (rtw_select_and_join_from_scanned_queue(pmlmepriv) == _SUCCESS) { /*_set_timer(&pmlmepriv->assoc_timer, MAX_JOIN_TIMEOUT);*/ set_assoc_timer(pmlmepriv, MAX_JOIN_TIMEOUT); } else { WLAN_BSSID_EX *pdev_network = &(adapter->registrypriv.dev_network); u8 *pibss = adapter->registrypriv.dev_network.MacAddress; /* pmlmepriv->fw_state ^= WIFI_UNDER_SURVEY; */ /* because don't set assoc_timer */ _clr_fwstate_(pmlmepriv, WIFI_UNDER_SURVEY); _rtw_memset(&pdev_network->Ssid, 0, sizeof(NDIS_802_11_SSID)); _rtw_memcpy(&pdev_network->Ssid, &pmlmepriv->assoc_ssid, sizeof(NDIS_802_11_SSID)); rtw_update_registrypriv_dev_network(adapter); rtw_generate_random_ibss(pibss); /*pmlmepriv->fw_state = WIFI_ADHOC_MASTER_STATE;*/ init_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE); if (rtw_create_ibss_cmd(adapter, 0) != _SUCCESS) RTW_ERR("rtw_create_ibss_cmd FAIL\n"); pmlmepriv->to_join = _FALSE; } } } else { int s_ret; set_fwstate(pmlmepriv, WIFI_UNDER_LINKING); pmlmepriv->to_join = _FALSE; s_ret = rtw_select_and_join_from_scanned_queue(pmlmepriv); if (_SUCCESS == s_ret) { /*_set_timer(&pmlmepriv->assoc_timer, MAX_JOIN_TIMEOUT);*/ set_assoc_timer(pmlmepriv, MAX_JOIN_TIMEOUT); } else if (s_ret == 2) { /* there is no need to wait for join */ _clr_fwstate_(pmlmepriv, WIFI_UNDER_LINKING); rtw_indicate_connect(adapter); } else { RTW_INFO("try_to_join, but select scanning queue fail, to_roam:%d\n", rtw_to_roam(adapter)); if (rtw_to_roam(adapter) != 0) { struct sitesurvey_parm scan_parm; u8 ssc_chk = rtw_sitesurvey_condition_check(adapter, _FALSE); rtw_init_sitesurvey_parm(adapter, &scan_parm); _rtw_memcpy(&scan_parm.ssid[0], &pmlmepriv->assoc_ssid, sizeof(NDIS_802_11_SSID)); scan_parm.ssid_num = 1; if (rtw_dec_to_roam(adapter) == 0 || (ssc_chk != SS_ALLOW && ssc_chk != SS_DENY_BUSY_TRAFFIC) || _SUCCESS != rtw_sitesurvey_cmd(adapter, &scan_parm) ) { rtw_set_to_roam(adapter, 0); if (MLME_IS_ASOC(adapter) == _TRUE) rtw_free_assoc_resources(adapter, _TRUE); rtw_indicate_disconnect(adapter, 0, _FALSE); } else pmlmepriv->to_join = _TRUE; } else rtw_indicate_disconnect(adapter, 0, _FALSE); _clr_fwstate_(pmlmepriv, WIFI_UNDER_LINKING); } } } else { if (rtw_chk_roam_flags(adapter, RTW_ROAM_ACTIVE) #if (defined(CONFIG_RTW_WNM) && defined(CONFIG_RTW_80211R)) || rtw_wnm_btm_roam_triggered(adapter) #endif ) { if (MLME_IS_STA(adapter) && check_fwstate(pmlmepriv, WIFI_ASOC_STATE)) { if (rtw_select_roaming_candidate(pmlmepriv) == _SUCCESS) { #ifdef CONFIG_RTW_80211R rtw_ft_start_roam(adapter, (u8 *)pmlmepriv->roam_network->network.MacAddress); #else receive_disconnect(adapter, pmlmepriv->cur_network.network.MacAddress , WLAN_REASON_ACTIVE_ROAM, _FALSE); pmlmeinfo->disconnect_occurred_time = rtw_systime_to_ms(rtw_get_current_time()); pmlmeinfo->disconnect_code = DISCONNECTION_BY_DRIVER_DUE_TO_ROAMING; pmlmeinfo->wifi_reason_code = WLAN_REASON_UNSPECIFIED; #endif } } } } RTW_INFO("scan complete in %dms\n",rtw_get_passing_time_ms(pmlmepriv->scan_start_time)); _rtw_spinunlock_bh(&pmlmepriv->lock); #ifdef CONFIG_P2P_PS if (check_fwstate(pmlmepriv, WIFI_ASOC_STATE) == _TRUE) p2p_ps_wk_cmd(adapter, P2P_PS_SCAN_DONE, 0); #endif /* CONFIG_P2P_PS */ rtw_mi_os_xmit_schedule(adapter); #ifdef CONFIG_DRVEXT_MODULE_WSC drvext_surveydone_callback(&adapter->drvextpriv); #endif #ifdef DBG_CONFIG_ERROR_DETECT { struct mlme_ext_priv *pmlmeext = &adapter->mlmeextpriv; if (pmlmeext->sitesurvey_res.bss_cnt == 0) { /* rtw_hal_sreset_reset(adapter); */ } } #endif #ifdef CONFIG_IOCTL_CFG80211 rtw_cfg80211_surveydone_event_callback(adapter); #endif /* CONFIG_IOCTL_CFG80211 */ rtw_indicate_scan_done(adapter, pmlmeext->scan_abort); #if defined(CONFIG_CONCURRENT_MODE) && defined(CONFIG_IOCTL_CFG80211) rtw_cfg80211_indicate_scan_done_for_buddy(adapter, _FALSE); #endif if (parm->activate_ch_cnt) { #ifdef CONFIG_IOCTL_CFG80211 struct get_chplan_resp *chplan; if (rtw_get_chplan_cmd(adapter, RTW_CMDF_DIRECTLY, &chplan) != _SUCCESS || rtw_regd_change_complete_async(adapter_to_wiphy(adapter), chplan) != _SUCCESS) rtw_warn_on(1); #endif op_class_pref_apply_regulatory(adapter, REG_BEACON_HINT); rtw_nlrtw_reg_beacon_hint_event(adapter); } #ifdef CONFIG_RTW_MESH #if CONFIG_RTW_MESH_OFFCH_CAND if (rtw_mesh_offch_candidate_accepted(adapter)) { u8 ch; ch = rtw_mesh_select_operating_ch(adapter); if (ch && pmlmepriv->cur_network.network.Configuration.DSConfig != ch) { u8 ifbmp = rtw_mi_get_ap_mesh_ifbmp(adapter); if (ifbmp) { /* switch to selected channel */ rtw_change_bss_chbw_cmd(adapter, RTW_CMDF_DIRECTLY, ifbmp, 0, ch, REQ_BW_ORI, REQ_OFFSET_NONE); issue_probereq_ex(adapter, &pmlmepriv->cur_network.network.mesh_id, NULL, 0, 0, 0, 0); } else rtw_warn_on(1); } } #endif #endif /* CONFIG_RTW_MESH */ #ifdef CONFIG_RTW_ACS if (parm->acs) { u8 ifbmp = rtw_mi_get_ap_mesh_ifbmp(adapter); if (ifbmp) rtw_change_bss_chbw_cmd(adapter, RTW_CMDF_DIRECTLY, ifbmp, 0, REQ_CH_INT_INFO, REQ_BW_ORI, REQ_OFFSET_NONE); } #endif } u8 _rtw_sitesurvey_condition_check(const char *caller, _adapter *adapter, bool check_sc_interval) { u8 ss_condition = SS_ALLOW; struct mlme_priv *pmlmepriv = &adapter->mlmepriv; struct registry_priv *registry_par = &adapter->registrypriv; #ifdef CONFIG_MP_INCLUDED if (rtw_mp_mode_check(adapter)) { RTW_INFO("%s ("ADPT_FMT") MP mode block Scan request\n", caller, ADPT_ARG(adapter)); ss_condition = SS_DENY_MP_MODE; goto _exit; } #endif #ifdef DBG_LA_MODE if(registry_par->la_mode_en == 1 && MLME_IS_ASOC(adapter)) { RTW_INFO("%s ("ADPT_FMT") LA debug mode block Scan request\n", caller, ADPT_ARG(adapter)); ss_condition = SS_DENY_LA_MODE; goto _exit; } #endif #ifdef CONFIG_IOCTL_CFG80211 if (adapter_wdev_data(adapter)->block_scan == _TRUE) { RTW_INFO("%s ("ADPT_FMT") wdev_priv.block_scan is set\n", caller, ADPT_ARG(adapter)); ss_condition = SS_DENY_BLOCK_SCAN; goto _exit; } #endif if (adapter_to_dvobj(adapter)->scan_deny == _TRUE) { RTW_INFO("%s ("ADPT_FMT") tpt mode, scan deny!\n", caller, ADPT_ARG(adapter)); ss_condition = SS_DENY_BLOCK_SCAN; goto _exit; } if (rtw_is_scan_deny(adapter)) { RTW_INFO("%s ("ADPT_FMT") : scan deny\n", caller, ADPT_ARG(adapter)); ss_condition = SS_DENY_BY_DRV; goto _exit; } #if 0 /*GEORGIA_TODO_FIXIT*/ if (adapter_to_rfctl(adapter)->adaptivity_en && rtw_hal_get_phy_edcca_flag(adapter) && rtw_is_2g_ch(GET_PHL_COM(adapter_to_dvobj(adapter))->current_channel)) { RTW_WARN(FUNC_ADPT_FMT": Adaptivity block scan! (ch=%u)\n", FUNC_ADPT_ARG(adapter), GET_PHL_COM(adapter_to_dvobj(adapter))->current_channel); ss_condition = SS_DENY_ADAPTIVITY; goto _exit; } #endif if (check_fwstate(pmlmepriv, WIFI_AP_STATE)){ if(check_fwstate(pmlmepriv, WIFI_UNDER_WPS)) { RTW_INFO("%s ("ADPT_FMT") : scan abort!! AP mode process WPS\n", caller, ADPT_ARG(adapter)); ss_condition = SS_DENY_SELF_AP_UNDER_WPS; goto _exit; } else if (check_fwstate(pmlmepriv, WIFI_UNDER_LINKING) == _TRUE) { RTW_INFO("%s ("ADPT_FMT") : scan abort!!AP mode under linking (fwstate=0x%x)\n", caller, ADPT_ARG(adapter), pmlmepriv->fw_state); ss_condition = SS_DENY_SELF_AP_UNDER_LINKING; goto _exit; } else if (check_fwstate(pmlmepriv, WIFI_UNDER_SURVEY) == _TRUE) { RTW_INFO("%s ("ADPT_FMT") : scan abort!!AP mode under survey (fwstate=0x%x)\n", caller, ADPT_ARG(adapter), pmlmepriv->fw_state); ss_condition = SS_DENY_SELF_AP_UNDER_SURVEY; goto _exit; } } else { if (check_fwstate(pmlmepriv, WIFI_UNDER_LINKING) == _TRUE) { RTW_INFO("%s ("ADPT_FMT") : scan abort!!STA mode under linking (fwstate=0x%x)\n", caller, ADPT_ARG(adapter), pmlmepriv->fw_state); ss_condition = SS_DENY_SELF_STA_UNDER_LINKING; goto _exit; } else if (check_fwstate(pmlmepriv, WIFI_UNDER_SURVEY) == _TRUE) { RTW_INFO("%s ("ADPT_FMT") : scan abort!!STA mode under survey (fwstate=0x%x)\n", caller, ADPT_ARG(adapter), pmlmepriv->fw_state); ss_condition = SS_DENY_SELF_STA_UNDER_SURVEY; goto _exit; } } #ifdef CONFIG_CONCURRENT_MODE if (rtw_mi_buddy_check_fwstate(adapter, WIFI_UNDER_LINKING | WIFI_UNDER_WPS)) { RTW_INFO("%s ("ADPT_FMT") : scan abort!! buddy_intf under linking or wps\n", caller, ADPT_ARG(adapter)); ss_condition = SS_DENY_BUDDY_UNDER_LINK_WPS; goto _exit; } else if (rtw_mi_buddy_check_fwstate(adapter, WIFI_UNDER_SURVEY)) { RTW_INFO("%s ("ADPT_FMT") : scan abort!! buddy_intf under survey\n", caller, ADPT_ARG(adapter)); ss_condition = SS_DENY_BUDDY_UNDER_SURVEY; goto _exit; } #endif /* CONFIG_CONCURRENT_MODE */ #ifdef RTW_BUSY_DENY_SCAN /* * busy traffic check * Rules: * 1. If (scan interval <= BUSY_TRAFFIC_SCAN_DENY_PERIOD) always allow * scan, otherwise goto rule 2. * 2. Deny scan if any interface is busy, otherwise allow scan. */ if (pmlmepriv->lastscantime && (rtw_get_passing_time_ms(pmlmepriv->lastscantime) > registry_par->scan_interval_thr) && rtw_mi_busy_traffic_check(adapter)) { RTW_WARN("%s ("ADPT_FMT") : scan abort!! BusyTraffic\n", caller, ADPT_ARG(adapter)); ss_condition = SS_DENY_BUSY_TRAFFIC; goto _exit; } #endif /* RTW_BUSY_DENY_SCAN */ _exit: return ss_condition; } /*rtw_mlme_ext.c*/ void sitesurvey_set_offch_state(_adapter *adapter, u8 scan_state) { struct rf_ctl_t *rfctl = adapter_to_rfctl(adapter); _rtw_mutex_lock_interruptible(&rfctl->offch_mutex); switch (scan_state) { case SCAN_DISABLE: case SCAN_BACK_OP: rfctl->offch_state = OFFCHS_NONE; break; case SCAN_START: case SCAN_LEAVING_OP: rfctl->offch_state = OFFCHS_LEAVING_OP; break; case SCAN_ENTER: case SCAN_LEAVE_OP: rfctl->offch_state = OFFCHS_LEAVE_OP; break; case SCAN_COMPLETE: case SCAN_BACKING_OP: rfctl->offch_state = OFFCHS_BACKING_OP; break; default: break; } _rtw_mutex_unlock(&rfctl->offch_mutex); } static u8 rtw_scan_sparse(_adapter *adapter, struct rtw_ieee80211_channel *ch, u8 ch_num) { /* interval larger than this is treated as backgroud scan */ #ifndef RTW_SCAN_SPARSE_BG_INTERVAL_MS #define RTW_SCAN_SPARSE_BG_INTERVAL_MS 12000 #endif #ifndef RTW_SCAN_SPARSE_CH_NUM_MIRACAST #define RTW_SCAN_SPARSE_CH_NUM_MIRACAST 1 #endif #ifndef RTW_SCAN_SPARSE_CH_NUM_BG #define RTW_SCAN_SPARSE_CH_NUM_BG 4 #endif #define SCAN_SPARSE_CH_NUM_INVALID 255 static u8 token = 255; u32 interval; bool busy_traffic = _FALSE; bool miracast_enabled = _FALSE; bool bg_scan = _FALSE; u8 max_allow_ch = SCAN_SPARSE_CH_NUM_INVALID; u8 scan_division_num; u8 ret_num = ch_num; struct mlme_ext_priv *mlmeext = &adapter->mlmeextpriv; if (mlmeext->last_scan_time == 0) mlmeext->last_scan_time = rtw_get_current_time(); interval = rtw_get_passing_time_ms(mlmeext->last_scan_time); if (rtw_mi_busy_traffic_check(adapter)) busy_traffic = _TRUE; if (rtw_mi_check_miracast_enabled(adapter)) miracast_enabled = _TRUE; if (interval > RTW_SCAN_SPARSE_BG_INTERVAL_MS) bg_scan = _TRUE; /* max_allow_ch by conditions*/ #if RTW_SCAN_SPARSE_MIRACAST if (miracast_enabled == _TRUE && busy_traffic == _TRUE) max_allow_ch = rtw_min(max_allow_ch, RTW_SCAN_SPARSE_CH_NUM_MIRACAST); #endif #if RTW_SCAN_SPARSE_BG if (bg_scan == _TRUE) max_allow_ch = rtw_min(max_allow_ch, RTW_SCAN_SPARSE_CH_NUM_BG); #endif if (max_allow_ch != SCAN_SPARSE_CH_NUM_INVALID) { int i; int k = 0; scan_division_num = (ch_num / max_allow_ch) + ((ch_num % max_allow_ch) ? 1 : 0); token = (token + 1) % scan_division_num; if (0) RTW_INFO("scan_division_num:%u, token:%u\n", scan_division_num, token); for (i = 0; i < ch_num; i++) { if (ch[i].hw_value && (i % scan_division_num) == token ) { if (i != k) _rtw_memcpy(&ch[k], &ch[i], sizeof(struct rtw_ieee80211_channel)); k++; } } _rtw_memset(&ch[k], 0, sizeof(struct rtw_ieee80211_channel)); ret_num = k; mlmeext->last_scan_time = rtw_get_current_time(); } return ret_num; } static int rtw_scan_ch_decision(_adapter *padapter, struct rtw_ieee80211_channel *out, u32 out_num, struct rtw_ieee80211_channel *in, u32 in_num, bool no_sparse) { int i, j; int set_idx; u8 chan; struct rf_ctl_t *rfctl = adapter_to_rfctl(padapter); struct registry_priv *regsty = dvobj_to_regsty(adapter_to_dvobj(padapter)); /* clear first */ _rtw_memset(out, 0, sizeof(struct rtw_ieee80211_channel) * out_num); /* acquire channels from in */ j = 0; for (i = 0; i < in_num; i++) { if (0) RTW_INFO(FUNC_ADPT_FMT" "CHAN_FMT"\n", FUNC_ADPT_ARG(padapter), CHAN_ARG(&in[i])); if (!in[i].hw_value || (in[i].flags & RTW_IEEE80211_CHAN_DISABLED)) continue; if (rtw_mlme_band_check(padapter, in[i].hw_value) == _FALSE) continue; set_idx = rtw_chset_search_ch(rfctl->channel_set, in[i].hw_value); if (set_idx >= 0) { if (j >= out_num) { RTW_PRINT(FUNC_ADPT_FMT" out_num:%u not enough\n", FUNC_ADPT_ARG(padapter), out_num); break; } _rtw_memcpy(&out[j], &in[i], sizeof(struct rtw_ieee80211_channel)); if (rfctl->channel_set[set_idx].flags & (RTW_CHF_NO_IR | RTW_CHF_DFS)) out[j].flags |= RTW_IEEE80211_CHAN_PASSIVE_SCAN; j++; } if (j >= out_num) break; } /* if out is empty, use channel_set as default */ if (j == 0) { for (i = 0; i < rfctl->max_chan_nums; i++) { chan = rfctl->channel_set[i].ChannelNum; if (rtw_mlme_band_check(padapter, chan) == _TRUE) { if (rtw_mlme_ignore_chan(padapter, chan) == _TRUE) continue; if (0) RTW_INFO(FUNC_ADPT_FMT" ch:%u\n", FUNC_ADPT_ARG(padapter), chan); if (j >= out_num) { RTW_PRINT(FUNC_ADPT_FMT" out_num:%u not enough\n", FUNC_ADPT_ARG(padapter), out_num); break; } out[j].hw_value = chan; if (rfctl->channel_set[i].flags & (RTW_CHF_NO_IR | RTW_CHF_DFS)) out[j].flags |= RTW_IEEE80211_CHAN_PASSIVE_SCAN; j++; } } } if (!no_sparse && !regsty->wifi_spec && j > 6 /* assume ch_num > 6 is normal scan */ ) { /* scan_sparse */ j = rtw_scan_sparse(padapter, out, j); } return j; } #ifdef CONFIG_SCAN_BACKOP u8 rtw_scan_backop_decision(_adapter *adapter) { struct mlme_ext_priv *mlmeext = &adapter->mlmeextpriv; struct mi_state mstate; u8 backop_flags = 0; rtw_mi_status(adapter, &mstate); if ((MSTATE_STA_LD_NUM(&mstate) && mlmeext_chk_scan_backop_flags_sta(mlmeext, SS_BACKOP_EN)) || (MSTATE_STA_NUM(&mstate) && mlmeext_chk_scan_backop_flags_sta(mlmeext, SS_BACKOP_EN_NL))) backop_flags |= mlmeext_scan_backop_flags_sta(mlmeext); #ifdef CONFIG_AP_MODE if ((MSTATE_AP_LD_NUM(&mstate) && mlmeext_chk_scan_backop_flags_ap(mlmeext, SS_BACKOP_EN)) || (MSTATE_AP_NUM(&mstate) && mlmeext_chk_scan_backop_flags_ap(mlmeext, SS_BACKOP_EN_NL))) backop_flags |= mlmeext_scan_backop_flags_ap(mlmeext); #endif #ifdef CONFIG_RTW_MESH if ((MSTATE_MESH_LD_NUM(&mstate) && mlmeext_chk_scan_backop_flags_mesh(mlmeext, SS_BACKOP_EN)) || (MSTATE_MESH_NUM(&mstate) && mlmeext_chk_scan_backop_flags_mesh(mlmeext, SS_BACKOP_EN_NL))) backop_flags |= mlmeext_scan_backop_flags_mesh(mlmeext); #endif return backop_flags; } #endif #if 0 /*#ifndef CONFIG_PHL_ARCH*/ void survey_timer_hdl(void *ctx) { _adapter *padapter = (_adapter *)ctx; struct cmd_obj *cmd; struct sitesurvey_parm *psurveyPara; struct cmd_priv *pcmdpriv = &adapter_to_dvobj(padapter)->cmdpriv; struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv; if (mlmeext_scan_state(pmlmeext) > SCAN_DISABLE) { cmd = (struct cmd_obj *)rtw_zmalloc(sizeof(struct cmd_obj)); if (cmd == NULL) { rtw_warn_on(1); goto exit; } cmd->padapter = padapter; psurveyPara = (struct sitesurvey_parm *)rtw_zmalloc(sizeof(struct sitesurvey_parm)); if (psurveyPara == NULL) { rtw_warn_on(1); rtw_mfree((unsigned char *)cmd, sizeof(struct cmd_obj)); goto exit; } init_h2fwcmd_w_parm_no_rsp(cmd, psurveyPara, CMD_SITE_SURVEY); rtw_enqueue_cmd(pcmdpriv, cmd); } exit: return; } static const char *const _scan_state_str[] = { "SCAN_DISABLE", "SCAN_START", "SCAN_PS_ANNC_WAIT", "SCAN_ENTER", "SCAN_PROCESS", "SCAN_BACKING_OP", "SCAN_BACK_OP", "SCAN_LEAVING_OP", "SCAN_LEAVE_OP", "SCAN_SW_ANTDIV_BL", "SCAN_TO_P2P_LISTEN", "SCAN_P2P_LISTEN", "SCAN_COMPLETE", "SCAN_STATE_MAX", }; void rtw_survey_cmd_callback(_adapter *padapter , struct cmd_obj *pcmd) { struct mlme_priv *pmlmepriv = &padapter->mlmepriv; if (pcmd->res == H2C_DROPPED) { /* TODO: cancel timer and do timeout handler directly... */ /* need to make timeout handlerOS independent */ mlme_set_scan_to_timer(pmlmepriv, 1); } else if (pcmd->res != H2C_SUCCESS) { mlme_set_scan_to_timer(pmlmepriv, 1); } /* free cmd */ rtw_free_cmd_obj(pcmd); } const char *scan_state_str(u8 state) { state = (state >= SCAN_STATE_MAX) ? SCAN_STATE_MAX : state; return _scan_state_str[state]; } static bool scan_abort_hdl(_adapter *adapter) { struct mlme_ext_priv *pmlmeext = &adapter->mlmeextpriv; struct ss_res *ss = &pmlmeext->sitesurvey_res; #ifdef CONFIG_P2P struct wifidirect_info *pwdinfo = &adapter->wdinfo; #endif bool ret = _FALSE; if (pmlmeext->scan_abort == _TRUE) { #ifdef CONFIG_P2P if (!rtw_p2p_chk_state(&adapter->wdinfo, P2P_STATE_NONE)) { rtw_p2p_findphase_ex_set(pwdinfo, P2P_FINDPHASE_EX_MAX); ss->channel_idx = 3; RTW_INFO("%s idx:%d, cnt:%u\n", __FUNCTION__ , ss->channel_idx , pwdinfo->find_phase_state_exchange_cnt ); } else #endif { ss->channel_idx = ss->ch_num; RTW_INFO("%s idx:%d\n", __FUNCTION__ , ss->channel_idx ); } pmlmeext->scan_abort = _FALSE; ret = _TRUE; } return ret; } static void sitesurvey_res_reset(_adapter *adapter, struct sitesurvey_parm *parm) { struct ss_res *ss = &adapter->mlmeextpriv.sitesurvey_res; RT_CHANNEL_INFO *chset = adapter_to_chset(adapter); int i; ss->bss_cnt = 0; ss->activate_ch_cnt = 0; ss->channel_idx = 0; ss->force_ssid_scan = 0; ss->igi_scan = 0; ss->igi_before_scan = 0; #ifdef CONFIG_SCAN_BACKOP ss->scan_cnt = 0; #endif #if defined(CONFIG_ANTENNA_DIVERSITY) || defined(DBG_SCAN_SW_ANTDIV_BL) ss->is_sw_antdiv_bl_scan = 0; #endif ss->ssid_num = 0; for (i = 0; i < RTW_SSID_SCAN_AMOUNT; i++) { if (parm->ssid[i].SsidLength) { _rtw_memcpy(ss->ssid[i].Ssid, parm->ssid[i].Ssid, IW_ESSID_MAX_SIZE); ss->ssid[i].SsidLength = parm->ssid[i].SsidLength; ss->ssid_num++; } else ss->ssid[i].SsidLength = 0; } ss->ch_num = rtw_scan_ch_decision(adapter , ss->ch, RTW_CHANNEL_SCAN_AMOUNT , parm->ch, parm->ch_num , parm->acs ); for (i = 0; i < MAX_CHANNEL_NUM; i++) chset[i].hidden_bss_cnt = 0; ss->bw = parm->bw; ss->igi = parm->igi; ss->token = parm->token; ss->duration = parm->duration; ss->scan_mode = parm->scan_mode; ss->token = parm->token; ss->acs = parm->acs; } static u8 sitesurvey_pick_ch_behavior(_adapter *padapter, u8 *ch, enum rtw_phl_scan_type *type) { u8 next_state; u8 scan_ch = 0; enum rtw_phl_scan_type stype = RTW_PHL_SCAN_PASSIVE; struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv; struct ss_res *ss = &pmlmeext->sitesurvey_res; struct rf_ctl_t *rfctl = adapter_to_rfctl(padapter); int ch_set_idx; #ifdef CONFIG_P2P struct wifidirect_info *pwdinfo = &padapter->wdinfo; #endif #ifdef CONFIG_SCAN_BACKOP u8 backop_flags = 0; #endif /* handle scan abort request */ scan_abort_hdl(padapter); #ifdef CONFIG_P2P if (pwdinfo->rx_invitereq_info.scan_op_ch_only || pwdinfo->p2p_info.scan_op_ch_only) { if (pwdinfo->rx_invitereq_info.scan_op_ch_only) scan_ch = pwdinfo->rx_invitereq_info.operation_ch[ss->channel_idx]; else scan_ch = pwdinfo->p2p_info.operation_ch[ss->channel_idx]; stype = RTW_PHL_SCAN_ACTIVE; } else if (rtw_p2p_findphase_ex_is_social(pwdinfo)) { /* * Commented by Albert 2011/06/03 * The driver is in the find phase, it should go through the social channel. */ scan_ch = pwdinfo->social_chan[ss->channel_idx]; ch_set_idx = rtw_chset_search_ch(rfctl->channel_set, scan_ch); if (ch_set_idx >= 0) stype = rfctl->channel_set[ch_set_idx].flags & RTW_CHF_NO_IR ? RTW_PHL_SCAN_PASSIVE : RTW_PHL_SCAN_ACTIVE; else stype = RTW_PHL_SCAN_ACTIVE; } else #endif /* CONFIG_P2P */ { struct rtw_ieee80211_channel *ch; #ifdef CONFIG_SCAN_BACKOP backop_flags = rtw_scan_backop_decision(padapter); #endif #ifdef CONFIG_SCAN_BACKOP if (!(backop_flags && ss->scan_cnt >= ss->scan_cnt_max)) #endif { #ifdef CONFIG_RTW_WIFI_HAL if (adapter_to_dvobj(padapter)->nodfs) { while (ss->channel_idx < ss->ch_num && rtw_chset_is_dfs_ch(rfctl->channel_set, ss->ch[ss->channel_idx].hw_value)) ss->channel_idx++; } else #endif if (ss->channel_idx != 0 && ss->force_ssid_scan == 0 && pmlmeext->sitesurvey_res.ssid_num && (ss->ch[ss->channel_idx - 1].flags & RTW_IEEE80211_CHAN_PASSIVE_SCAN) ) { ch_set_idx = rtw_chset_search_ch(rfctl->channel_set, ss->ch[ss->channel_idx - 1].hw_value); if (ch_set_idx != -1 && rfctl->channel_set[ch_set_idx].hidden_bss_cnt && (!IS_DFS_SLAVE_WITH_RD(rfctl) || rtw_rfctl_dfs_domain_unknown(rfctl) || !CH_IS_NON_OCP(&rfctl->channel_set[ch_set_idx])) ) { ss->channel_idx--; ss->force_ssid_scan = 1; } } else ss->force_ssid_scan = 0; } if (ss->channel_idx < ss->ch_num) { ch = &ss->ch[ss->channel_idx]; scan_ch = ch->hw_value; #if defined(CONFIG_RTW_ACS) && defined(CONFIG_RTW_ACS_DBG) if (IS_ACS_ENABLE(padapter) && rtw_is_acs_passiv_scan(padapter)) stype = RTW_PHL_SCAN_PASSIVE; else #endif /*CONFIG_RTW_ACS*/ stype = (ch->flags & RTW_IEEE80211_CHAN_PASSIVE_SCAN) ? RTW_PHL_SCAN_PASSIVE : RTW_PHL_SCAN_ACTIVE; } } if (scan_ch != 0) { next_state = SCAN_PROCESS; #ifdef CONFIG_SCAN_BACKOP if (backop_flags) { if (ss->scan_cnt < ss->scan_cnt_max) ss->scan_cnt++; else { mlmeext_assign_scan_backop_flags(pmlmeext, backop_flags); next_state = SCAN_BACKING_OP; } } #endif } else if (rtw_p2p_findphase_ex_is_needed(pwdinfo)) { /* go p2p listen */ next_state = SCAN_TO_P2P_LISTEN; #ifdef CONFIG_ANTENNA_DIVERSITY } else if (rtw_hal_antdiv_before_linked(padapter)) { /* go sw antdiv before link */ next_state = SCAN_SW_ANTDIV_BL; #endif } else { next_state = SCAN_COMPLETE; #if defined(DBG_SCAN_SW_ANTDIV_BL) { /* for SCAN_SW_ANTDIV_BL state testing */ struct dvobj_priv *dvobj = adapter_to_dvobj(padapter); int i; bool is_linked = _FALSE; for (i = 0; i < dvobj->iface_nums; i++) { if (rtw_linked_check(dvobj->padapters[i])) is_linked = _TRUE; } if (!is_linked) { static bool fake_sw_antdiv_bl_state = 0; if (fake_sw_antdiv_bl_state == 0) { next_state = SCAN_SW_ANTDIV_BL; fake_sw_antdiv_bl_state = 1; } else fake_sw_antdiv_bl_state = 0; } } #endif /* defined(DBG_SCAN_SW_ANTDIV_BL) */ } #ifdef CONFIG_SCAN_BACKOP if (next_state != SCAN_PROCESS) ss->scan_cnt = 0; #endif #ifdef DBG_FIXED_CHAN if (pmlmeext->fixed_chan != 0xff && next_state == SCAN_PROCESS) scan_ch = pmlmeext->fixed_chan; #endif if (ch) *ch = scan_ch; if (type) *type = stype; return next_state; } void site_survey(_adapter *padapter, u8 survey_channel, enum rtw_phl_scan_type ScanType) { struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv; struct ss_res *ss = &pmlmeext->sitesurvey_res; u8 ssid_scan = 0; #ifdef CONFIG_P2P #ifndef CONFIG_IOCTL_CFG80211 struct wifidirect_info *pwdinfo = &(padapter->wdinfo); #endif #endif if (survey_channel != 0) { set_channel_bwmode(padapter, survey_channel, CHAN_OFFSET_NO_EXT, CHANNEL_WIDTH_20, _FALSE); if (ScanType == RTW_PHL_SCAN_PASSIVE && ss->force_ssid_scan) ssid_scan = 1; else if (ScanType == RTW_PHL_SCAN_ACTIVE) { #ifdef CONFIG_P2P #ifdef CONFIG_IOCTL_CFG80211 if (rtw_cfg80211_is_p2p_scan(padapter)) #else if (rtw_p2p_chk_state(pwdinfo, P2P_STATE_SCAN) || rtw_p2p_chk_state(pwdinfo, P2P_STATE_FIND_PHASE_SEARCH)) #endif { issue_probereq_p2p(padapter, NULL); issue_probereq_p2p(padapter, NULL); issue_probereq_p2p(padapter, NULL); } else #endif /* CONFIG_P2P */ { if (pmlmeext->sitesurvey_res.scan_mode == RTW_PHL_SCAN_ACTIVE) { /* IOT issue, When wifi_spec is not set, send one probe req without WPS IE. */ if (padapter->registrypriv.wifi_spec) issue_probereq(padapter, NULL, NULL); else issue_probereq_ex(padapter, NULL, NULL, 0, 0, 0, 0); issue_probereq(padapter, NULL, NULL); } ssid_scan = 1; } } if (ssid_scan) { int i; for (i = 0; i < RTW_SSID_SCAN_AMOUNT; i++) { if (pmlmeext->sitesurvey_res.ssid[i].SsidLength) { /* IOT issue, When wifi_spec is not set, send one probe req without WPS IE. */ if (padapter->registrypriv.wifi_spec) issue_probereq(padapter, &(pmlmeext->sitesurvey_res.ssid[i]), NULL); else issue_probereq_ex(padapter, &(pmlmeext->sitesurvey_res.ssid[i]), NULL, 0, 0, 0, 0); issue_probereq(padapter, &(pmlmeext->sitesurvey_res.ssid[i]), NULL); } } } } else { /* channel number is 0 or this channel is not valid. */ rtw_warn_on(1); } return; } void survey_done_set_ch_bw(_adapter *padapter) { struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv; u8 cur_channel = 0; u8 cur_bwmode; u8 cur_ch_offset; if (rtw_mi_get_ch_setting_union(padapter, &cur_channel, &cur_bwmode, &cur_ch_offset) != 0) { if (0) RTW_INFO(FUNC_ADPT_FMT" back to linked/linking union - ch:%u, bw:%u, offset:%u\n", FUNC_ADPT_ARG(padapter), cur_channel, cur_bwmode, cur_ch_offset); } else { #ifdef CONFIG_P2P struct dvobj_priv *dvobj = adapter_to_dvobj(padapter); _adapter *iface; int i; for (i = 0; i < dvobj->iface_nums; i++) { iface = dvobj->padapters[i]; if (!iface) continue; #ifdef CONFIG_IOCTL_CFG80211 if (iface->wdinfo.driver_interface == DRIVER_CFG80211 && !adapter_wdev_data(iface)->p2p_enabled) continue; #endif if (rtw_p2p_chk_state(&iface->wdinfo, P2P_STATE_LISTEN)) { cur_channel = iface->wdinfo.listen_channel; cur_bwmode = CHANNEL_WIDTH_20; cur_ch_offset = CHAN_OFFSET_NO_EXT; if (0) RTW_INFO(FUNC_ADPT_FMT" back to "ADPT_FMT"'s listen ch - ch:%u, bw:%u, offset:%u\n", FUNC_ADPT_ARG(padapter), ADPT_ARG(iface), cur_channel, cur_bwmode, cur_ch_offset); break; } } #endif /* CONFIG_P2P */ if (cur_channel == 0) { cur_channel = pmlmeext->chandef.chan; cur_bwmode = pmlmeext->chandef.bw; cur_ch_offset = pmlmeext->chandef.offset; if (0) RTW_INFO(FUNC_ADPT_FMT" back to ch:%u, bw:%u, offset:%u\n", FUNC_ADPT_ARG(padapter), cur_channel, cur_bwmode, cur_ch_offset); } } set_channel_bwmode(padapter, cur_channel, cur_ch_offset, cur_bwmode, _FALSE); } void sitesurvey_set_igi(_adapter *adapter) { struct mlme_ext_priv *mlmeext = &adapter->mlmeextpriv; struct ss_res *ss = &mlmeext->sitesurvey_res; u8 igi; #ifdef CONFIG_P2P struct wifidirect_info *pwdinfo = &adapter->wdinfo; #endif switch (mlmeext_scan_state(mlmeext)) { case SCAN_ENTER: #ifdef CONFIG_P2P #ifdef CONFIG_IOCTL_CFG80211 if (pwdinfo->driver_interface == DRIVER_CFG80211 && rtw_cfg80211_is_p2p_scan(adapter)) igi = 0x30; else #endif /* CONFIG_IOCTL_CFG80211 */ if (!rtw_p2p_chk_state(pwdinfo, P2P_STATE_NONE)) igi = 0x28; else #endif /* CONFIG_P2P */ if (ss->igi) igi = ss->igi; else #if defined(CONFIG_RTW_ACS) && defined(CONFIG_RTW_ACS_DBG) if (IS_ACS_ENABLE(adapter) && rtw_is_acs_igi_valid(adapter)) igi = rtw_acs_get_adv_igi(adapter); else #endif /*CONFIG_RTW_ACS*/ igi = 0x1e; /* record IGI status */ ss->igi_scan = igi; rtw_hal_get_phydm_var(adapter, HAL_PHYDM_IGI, &ss->igi_before_scan, NULL); /* disable DIG and set IGI for scan */ rtw_hal_set_phydm_var(adapter, HAL_PHYDM_IGI, &igi, _FALSE); break; case SCAN_COMPLETE: case SCAN_TO_P2P_LISTEN: /* enable DIG and restore IGI */ igi = 0xff; rtw_hal_set_phydm_var(adapter, HAL_PHYDM_IGI, &igi, _FALSE); break; #ifdef CONFIG_SCAN_BACKOP case SCAN_BACKING_OP: /* write IGI for op channel when DIG is not enabled */ rtw_hal_set_phydm_var(adapter, HAL_PHYDM_IGI_W, &ss->igi_before_scan, _FALSE); break; case SCAN_LEAVE_OP: /* write IGI for scan when DIG is not enabled */ rtw_hal_set_phydm_var(adapter, HAL_PHYDM_IGI_W, &ss->igi_scan, _FALSE); break; #endif /* CONFIG_SCAN_BACKOP */ default: rtw_warn_on(1); break; } } void sitesurvey_set_msr(_adapter *adapter, bool enter) { u8 network_type; struct mlme_ext_priv *pmlmeext = &adapter->mlmeextpriv; struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info); if (enter) { /* set MSR to no link state */ network_type = _HW_STATE_NOLINK_; } else { network_type = pmlmeinfo->state & 0x3; } } u8 sitesurvey_cmd_hdl(_adapter *padapter, u8 *pbuf) { struct sitesurvey_parm *pparm = (struct sitesurvey_parm *)pbuf; #ifdef DBG_CHECK_FW_PS_STATE struct dvobj_priv *dvobj = padapter->dvobj; struct debug_priv *pdbgpriv = &dvobj->drv_dbg; #endif struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv; struct ss_res *ss = &pmlmeext->sitesurvey_res; #ifdef CONFIG_RTW_CFGVENDOR_RANDOM_MAC_OUI struct rtw_wdev_priv *pwdev_priv = adapter_wdev_data(padapter); #endif u8 val8; #ifdef CONFIG_P2P struct wifidirect_info *pwdinfo = &padapter->wdinfo; #endif #ifdef DBG_CHECK_FW_PS_STATE if (rtw_fw_ps_state(padapter) == _FAIL) { RTW_INFO("scan without leave 32k\n"); pdbgpriv->dbg_scan_pwr_state_cnt++; } #endif /* DBG_CHECK_FW_PS_STATE */ /* increase channel idx */ if (mlmeext_chk_scan_state(pmlmeext, SCAN_PROCESS)) ss->channel_idx++; /* update scan state to next state (assigned by previous cmd hdl) */ if (mlmeext_scan_state(pmlmeext) != mlmeext_scan_next_state(pmlmeext)) mlmeext_set_scan_state(pmlmeext, mlmeext_scan_next_state(pmlmeext)); operation_by_state: switch (mlmeext_scan_state(pmlmeext)) { case SCAN_DISABLE: /* * SW parameter initialization */ sitesurvey_res_reset(padapter, pparm); mlmeext_set_scan_state(pmlmeext, SCAN_START); goto operation_by_state; case SCAN_START: #ifdef CONFIG_RTW_CFGVENDOR_RANDOM_MAC_OUI if ((pwdev_priv->pno_mac_addr[0] != 0xFF) && (MLME_IS_STA(padapter)) && (check_fwstate(&padapter->mlmepriv, WIFI_ASOC_STATE) == _FALSE)) { u16 seq_num; rtw_hal_pno_random_gen_mac_addr(padapter); rtw_hal_set_hw_mac_addr(padapter, pwdev_priv->pno_mac_addr); get_random_bytes(&seq_num, 2); pwdev_priv->pno_scan_seq_num = seq_num & 0xFFF; RTW_INFO("%s pno_scan_seq_num %d\n", __func__, pwdev_priv->pno_scan_seq_num); } #endif /* * prepare to leave operating channel */ /* apply rx ampdu setting */ if (ss->rx_ampdu_accept != RX_AMPDU_ACCEPT_INVALID || ss->rx_ampdu_size != RX_AMPDU_SIZE_INVALID) rtw_rx_ampdu_apply(padapter); /* clear HW TX queue before scan */ rtw_hal_set_hwreg(padapter, HW_VAR_CHECK_TXBUF, 0); rtw_hal_macid_sleep_all_used(padapter); /* power save state announcement */ if (rtw_ps_annc(padapter, 1)) { mlmeext_set_scan_state(pmlmeext, SCAN_PS_ANNC_WAIT); mlmeext_set_scan_next_state(pmlmeext, SCAN_ENTER); set_survey_timer(pmlmeext, 50); /* delay 50ms to protect nulldata(1) */ } else { mlmeext_set_scan_state(pmlmeext, SCAN_ENTER); goto operation_by_state; } break; case SCAN_ENTER: /* * HW register and DM setting for enter scan */ rtw_phydm_ability_backup(padapter); sitesurvey_set_igi(padapter); /* config dynamic functions for off channel */ rtw_phydm_func_for_offchannel(padapter); /* set network type to no link state */ sitesurvey_set_msr(padapter, _TRUE); val8 = 1; /* under site survey */ rtw_hal_set_hwreg(padapter, HW_VAR_MLME_SITESURVEY, (u8 *)(&val8)); mlmeext_set_scan_state(pmlmeext, SCAN_PROCESS); goto operation_by_state; case SCAN_PROCESS: { u8 scan_ch; enum rtw_phl_scan_type stype; u8 next_state; u32 scan_ms; #ifdef CONFIG_RTW_ACS if (IS_ACS_ENABLE(padapter)) rtw_acs_get_rst(padapter); #endif next_state = sitesurvey_pick_ch_behavior(padapter, &scan_ch, &stype); if (next_state != SCAN_PROCESS) { mlmeext_set_scan_state(pmlmeext, next_state); goto operation_by_state; } /* still SCAN_PROCESS state */ #ifdef DBG_SITESURVEY #ifdef CONFIG_P2P RTW_INFO(FUNC_ADPT_FMT" %s ch:%u (cnt:%u,idx:%d) at %dms, %c%c%c%c\n" , FUNC_ADPT_ARG(padapter) , mlmeext_scan_state_str(pmlmeext) , scan_ch , pwdinfo->find_phase_state_exchange_cnt, ss->channel_idx , rtw_get_passing_time_ms(padapter->mlmepriv.scan_start_time) , stype ? 'A' : 'P', ss->scan_mode ? 'A' : 'P' , ss->ssid[0].SsidLength ? 'S' : ' ' , ss->force_ssid_scan ? 'F' : ' ' ); #else RTW_INFO(FUNC_ADPT_FMT" %s ch:%u (idx:%d) at %dms, %c%c%c%c\n" , FUNC_ADPT_ARG(padapter) , mlmeext_scan_state_str(pmlmeext) , scan_ch , ss->channel_idx , rtw_get_passing_time_ms(padapter->mlmepriv.scan_start_time) , stype ? 'A' : 'P', ss->scan_mode ? 'A' : 'P' , ss->ssid[0].SsidLength ? 'S' : ' ' , ss->force_ssid_scan ? 'F' : ' ' ); #endif /* CONFIG_P2P */ #endif /*DBG_SITESURVEY*/ #ifdef DBG_FIXED_CHAN if (pmlmeext->fixed_chan != 0xff) RTW_INFO(FUNC_ADPT_FMT" fixed_chan:%u\n", pmlmeext->fixed_chan); #endif site_survey(padapter, scan_ch, stype); #if defined(CONFIG_ATMEL_RC_PATCH) if (check_fwstate(pmlmepriv, WIFI_ASOC_STATE) == _TRUE) scan_ms = 20; else scan_ms = 40; #else #if defined(CONFIG_RTW_ACS) && defined(CONFIG_RTW_ACS_DBG) if (IS_ACS_ENABLE(padapter) && rtw_is_acs_st_valid(padapter)) scan_ms = rtw_acs_get_adv_st(padapter); else #endif /*CONFIG_RTW_ACS*/ scan_ms = ss->scan_ch_ms; #endif #if defined(CONFIG_ANTENNA_DIVERSITY) || defined(DBG_SCAN_SW_ANTDIV_BL) if (ss->is_sw_antdiv_bl_scan) scan_ms = scan_ms / 2; #endif #ifdef CONFIG_RTW_ACS if (IS_ACS_ENABLE(padapter)) { if (pparm->token) rtw_acs_trigger(padapter, scan_ms, scan_ch, NHM_PID_IEEE_11K_HIGH); else rtw_acs_trigger(padapter, scan_ms, scan_ch, NHM_PID_ACS); } #endif set_survey_timer(pmlmeext, scan_ms); break; } #ifdef CONFIG_SCAN_BACKOP case SCAN_BACKING_OP: { u8 back_ch, back_bw, back_ch_offset; u8 need_ch_setting_union = _TRUE; if (need_ch_setting_union) { if (rtw_mi_get_ch_setting_union(padapter, &back_ch, &back_bw, &back_ch_offset) == 0) { rtw_warn_on(1); back_ch = pmlmeext->chandef.chan; back_bw = pmlmeext->chandef.bw; back_ch_offset = pmlmeext->chandef.offset; } } #ifdef DBG_SITESURVEY RTW_INFO(FUNC_ADPT_FMT" %s ch:%u, bw:%u, offset:%u at %dms\n" , FUNC_ADPT_ARG(padapter) , mlmeext_scan_state_str(pmlmeext) , back_ch, back_bw, back_ch_offset , rtw_get_passing_time_ms(padapter->mlmepriv.scan_start_time) ); #endif /*DBG_SITESURVEY*/ set_channel_bwmode(padapter, back_ch, back_ch_offset, back_bw, _FALSE); sitesurvey_set_msr(padapter, _FALSE); val8 = 0; /* survey done */ rtw_hal_set_hwreg(padapter, HW_VAR_MLME_SITESURVEY, (u8 *)(&val8)); if (mlmeext_chk_scan_backop_flags(pmlmeext, SS_BACKOP_PS_ANNC)) { sitesurvey_set_igi(padapter); rtw_hal_macid_wakeup_all_used(padapter); rtw_ps_annc(padapter, 0); } mlmeext_set_scan_state(pmlmeext, SCAN_BACK_OP); ss->backop_time = rtw_get_current_time(); if (mlmeext_chk_scan_backop_flags(pmlmeext, SS_BACKOP_TX_RESUME)) rtw_mi_os_xmit_schedule(padapter); goto operation_by_state; } case SCAN_BACK_OP: if (rtw_get_passing_time_ms(ss->backop_time) >= ss->backop_ms || pmlmeext->scan_abort ) { mlmeext_set_scan_state(pmlmeext, SCAN_LEAVING_OP); goto operation_by_state; } set_survey_timer(pmlmeext, 50); break; case SCAN_LEAVING_OP: /* * prepare to leave operating channel */ /* clear HW TX queue before scan */ rtw_hal_set_hwreg(padapter, HW_VAR_CHECK_TXBUF, 0); rtw_hal_macid_sleep_all_used(padapter); if (mlmeext_chk_scan_backop_flags(pmlmeext, SS_BACKOP_PS_ANNC) && rtw_ps_annc(padapter, 1) ) { mlmeext_set_scan_state(pmlmeext, SCAN_PS_ANNC_WAIT); mlmeext_set_scan_next_state(pmlmeext, SCAN_LEAVE_OP); set_survey_timer(pmlmeext, 50); /* delay 50ms to protect nulldata(1) */ } else { mlmeext_set_scan_state(pmlmeext, SCAN_LEAVE_OP); goto operation_by_state; } break; case SCAN_LEAVE_OP: /* * HW register and DM setting for enter scan */ if (mlmeext_chk_scan_backop_flags(pmlmeext, SS_BACKOP_PS_ANNC)) sitesurvey_set_igi(padapter); sitesurvey_set_msr(padapter, _TRUE); val8 = 1; /* under site survey */ rtw_hal_set_hwreg(padapter, HW_VAR_MLME_SITESURVEY, (u8 *)(&val8)); mlmeext_set_scan_state(pmlmeext, SCAN_PROCESS); goto operation_by_state; #endif /* CONFIG_SCAN_BACKOP */ #if defined(CONFIG_ANTENNA_DIVERSITY) || defined(DBG_SCAN_SW_ANTDIV_BL) case SCAN_SW_ANTDIV_BL: /* * 20100721 * For SW antenna diversity before link, it needs to switch to another antenna and scan again. * It compares the scan result and select better one to do connection. */ ss->bss_cnt = 0; ss->channel_idx = 0; ss->is_sw_antdiv_bl_scan = 1; mlmeext_set_scan_next_state(pmlmeext, SCAN_PROCESS); set_survey_timer(pmlmeext, ss->scan_ch_ms); break; #endif #ifdef CONFIG_P2P case SCAN_TO_P2P_LISTEN: /* * Set the P2P State to the listen state of find phase * and set the current channel to the listen channel */ set_channel_bwmode(padapter, pwdinfo->listen_channel, CHAN_OFFSET_NO_EXT, CHANNEL_WIDTH_20, _FALSE); rtw_p2p_set_state(pwdinfo, P2P_STATE_FIND_PHASE_LISTEN); /* turn on phy-dynamic functions */ rtw_phydm_ability_restore(padapter); sitesurvey_set_igi(padapter); mlmeext_set_scan_state(pmlmeext, SCAN_P2P_LISTEN); _set_timer(&pwdinfo->find_phase_timer, (u32)((u32)pwdinfo->listen_dwell * 100)); break; case SCAN_P2P_LISTEN: mlmeext_set_scan_state(pmlmeext, SCAN_PROCESS); ss->channel_idx = 0; goto operation_by_state; #endif /* CONFIG_P2P */ case SCAN_COMPLETE: #ifdef CONFIG_RTW_CFGVENDOR_RANDOM_MAC_OUI rtw_hal_set_hw_mac_addr(padapter, adapter_mac_addr(padapter)); #endif #ifdef CONFIG_P2P if (rtw_p2p_chk_state(pwdinfo, P2P_STATE_SCAN) || rtw_p2p_chk_state(pwdinfo, P2P_STATE_FIND_PHASE_SEARCH) ) { #ifdef CONFIG_CONCURRENT_MODE if (pwdinfo->driver_interface == DRIVER_WEXT) { if (rtw_mi_check_status(padapter, MI_LINKED)) _set_timer(&pwdinfo->ap_p2p_switch_timer, 500); } #endif rtw_p2p_set_state(pwdinfo, rtw_p2p_pre_state(pwdinfo)); } rtw_p2p_findphase_ex_set(pwdinfo, P2P_FINDPHASE_EX_NONE); #endif /* CONFIG_P2P */ /* switch channel */ survey_done_set_ch_bw(padapter); sitesurvey_set_msr(padapter, _FALSE); val8 = 0; /* survey done */ rtw_hal_set_hwreg(padapter, HW_VAR_MLME_SITESURVEY, (u8 *)(&val8)); /* turn on phy-dynamic functions */ rtw_phydm_ability_restore(padapter); sitesurvey_set_igi(padapter); rtw_hal_macid_wakeup_all_used(padapter); rtw_ps_annc(padapter, 0); /* apply rx ampdu setting */ rtw_rx_ampdu_apply(padapter); mlmeext_set_scan_state(pmlmeext, SCAN_DISABLE); report_surveydone_event(padapter, ss->acs); #ifdef CONFIG_RTW_ACS if (IS_ACS_ENABLE(padapter)) rtw_acs_select_best_chan(padapter); #endif issue_action_BSSCoexistPacket(padapter); issue_action_BSSCoexistPacket(padapter); issue_action_BSSCoexistPacket(padapter); #ifdef CONFIG_RTW_80211K if (ss->token) rm_post_event(padapter, ss->token, RM_EV_survey_done); #endif /* CONFIG_RTW_80211K */ break; } return H2C_SUCCESS; } #else u8 sitesurvey_cmd_hdl(_adapter *padapter, u8 *pbuf) { RTW_ERR("%s executed??\n", __func__); rtw_warn_on(1); return 0; } void rtw_survey_cmd_callback(_adapter *padapter, struct cmd_obj *pcmd) { RTW_ERR("%s executed??\n", __func__); rtw_warn_on(1); } /* remain on channel priv */ #define ROCH_CH_READY 0x1 struct scan_priv { _adapter *padapter; /* for remain on channel callback */ struct wireless_dev *wdev; struct ieee80211_channel channel; u8 channel_type; unsigned int duration; u64 cookie; u8 restore_ch; u8 roch_step; #ifdef CONFIG_RTW_80211K u32 rrm_token; /* 80211k use it to identify caller */ #endif }; #ifdef CONFIG_CMD_SCAN static struct rtw_phl_scan_param *_alloc_phl_param(_adapter *adapter, u8 scan_ch_num) { struct rtw_phl_scan_param *phl_param = NULL; struct scan_priv *scan_priv = NULL; if (scan_ch_num == 0) { RTW_ERR("%s scan_ch_num = 0\n", __func__); goto _err_exit; } /*create mem of PHL Scan parameter*/ phl_param = rtw_zmalloc(sizeof(*phl_param)); if (phl_param == NULL) { RTW_ERR("%s alloc phl_param fail\n", __func__); goto _err_exit; } scan_priv = rtw_zmalloc(sizeof(*scan_priv)); if (scan_priv == NULL) { RTW_ERR("%s alloc scan_priv fail\n", __func__); goto _err_scanpriv; } scan_priv->padapter = adapter; phl_param->priv = scan_priv; phl_param->wifi_role = adapter->phl_role; phl_param->back_op_mode = SCAN_BKOP_NONE; phl_param->ch_sz = sizeof(struct phl_scan_channel) * (scan_ch_num + 1); phl_param->ch = rtw_zmalloc(phl_param->ch_sz); if (phl_param->ch == NULL) { RTW_ERR("%s: alloc phl scan ch fail\n", __func__); goto _err_param_ch; } return phl_param; _err_param_ch: if (scan_priv) rtw_mfree(scan_priv, sizeof(*scan_priv)); _err_scanpriv: if (phl_param) rtw_mfree(phl_param, sizeof(*phl_param)); _err_exit: rtw_warn_on(1); return phl_param; } static u8 _free_phl_param(_adapter *adapter, struct rtw_phl_scan_param *phl_param) { u8 res = _FAIL; if (!phl_param) return res; if (phl_param->ch) rtw_mfree(phl_param->ch, phl_param->ch_sz); if (phl_param->priv) rtw_mfree(phl_param->priv, sizeof(struct scan_priv)); rtw_mfree(phl_param, sizeof(struct rtw_phl_scan_param)); res = _SUCCESS; return res; } #endif /*CONFIG_CMD_SCAN*/ static int scan_issue_pbreq_cb(void *priv, struct rtw_phl_scan_param *param) { struct scan_priv *scan_priv = (struct scan_priv *)priv; _adapter *padapter = scan_priv->padapter; NDIS_802_11_SSID ssid; int i; /* active scan behavior */ if (padapter->registrypriv.wifi_spec) issue_probereq(padapter, NULL, NULL); else issue_probereq_ex(padapter, NULL, NULL, 0, 0, 0, 0); issue_probereq(padapter, NULL, NULL); for (i = 0; i < param->ssid_num; i++) { if (param->ssid[i].ssid_len == 0) continue; ssid.SsidLength = param->ssid[i].ssid_len; _rtw_memcpy(ssid.Ssid, ¶m->ssid[i].ssid, ssid.SsidLength); /* IOT issue, * Send one probe req without WPS IE, * when not wifi_spec */ if (padapter->registrypriv.wifi_spec) issue_probereq(padapter, &ssid, NULL); else issue_probereq_ex(padapter, &ssid, NULL, 0, 0, 0, 0); issue_probereq(padapter, &ssid, NULL); } return 0; } static int scan_complete_cb(void *priv, struct rtw_phl_scan_param *param) { struct scan_priv *scan_priv = (struct scan_priv *)priv; _adapter *padapter = scan_priv->padapter; struct mlme_priv *pmlmepriv = &padapter->mlmepriv; struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv; bool acs = _FALSE; int ret = _FAIL; if (!rtw_is_adapter_up(padapter)) goto _exit; mlmeext_set_scan_state(pmlmeext, SCAN_DISABLE); report_surveydone_event(padapter, acs, RTW_CMDF_DIRECTLY); ret = _SUCCESS; _exit: RTW_INFO(FUNC_ADPT_FMT" takes %d ms to scan %d/%d channels\n", FUNC_ADPT_ARG(padapter), param->total_scan_time, #ifdef CONFIG_CMD_SCAN param->ch_idx, #else param->ch_idx + 1, #endif param->ch_num); _rtw_scan_abort_check(padapter, __func__); #ifdef CONFIG_CMD_SCAN _free_phl_param(padapter, param); pmlmeext->sitesurvey_res.scan_param = NULL; #else rtw_mfree(scan_priv, sizeof(*scan_priv)); #endif return ret; } static int scan_start_cb(void *priv, struct rtw_phl_scan_param *param) { struct scan_priv *scan_priv = (struct scan_priv *)priv; _adapter *padapter = scan_priv->padapter; struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv; pmlmeext->sitesurvey_res.bss_cnt = 0; pmlmeext->sitesurvey_res.activate_ch_cnt = 0; //TODO remove mlmeext_set_scan_state(pmlmeext, SCAN_PROCESS); #ifdef CONFIG_CMD_SCAN pmlmeext->sitesurvey_res.scan_param = param; #endif return 0; } #ifdef CONFIG_P2P static int scan_issue_p2p_pbreq_cb(void *priv, struct rtw_phl_scan_param *param) { struct scan_priv *scan_priv = (struct scan_priv *)priv; _adapter *padapter = scan_priv->padapter; issue_probereq_p2p(padapter, NULL); issue_probereq_p2p(padapter, NULL); issue_probereq_p2p(padapter, NULL); return 0; } #endif static int scan_ch_ready_cb(void *priv, struct rtw_phl_scan_param *param) { struct scan_priv *scan_priv = (struct scan_priv *)priv; _adapter *padapter = scan_priv->padapter; RTW_INFO("%s ch:%d\n", __func__, param->scan_ch->channel); return 0; } static inline void _ps_announce(_adapter *adapter, bool ps) { RTW_INFO(FUNC_ADPT_FMT" issue_null(%d)\n", FUNC_ADPT_ARG(adapter), ps); if (MLME_IS_STA(adapter)) { if (is_client_associated_to_ap(adapter) == _TRUE) { /*issue_nulldata(adapter, NULL, ps, 3, 500);*/ issue_nulldata(adapter, NULL, ps, 1, 0); } } #ifdef CONFIG_RTW_MESH else if (MLME_IS_MESH(adapter)) { rtw_mesh_ps_annc(adapter, ps); } #endif } u8 scan_issu_null_data_cb(void *priv, u8 ridx, bool ps) { #ifdef CONFIG_CMD_SCAN struct dvobj_priv *obj = (struct dvobj_priv *)priv; #else struct scan_priv *scan_priv = (struct scan_priv *)priv; _adapter *padapter = scan_priv->padapter; struct dvobj_priv *obj = adapter_to_dvobj(padapter); #endif _adapter *iface = NULL; if (ridx >= CONFIG_IFACE_NUMBER) { RTW_ERR("%s ridx:%d invalid\n", __func__, ridx); rtw_warn_on(1); goto _error; } iface = obj->padapters[ridx]; if (!rtw_is_adapter_up(iface)) goto _error; _ps_announce(iface, ps); return _SUCCESS; _error: return _FAIL; } static struct rtw_phl_scan_ops scan_ops_cb = { .scan_start = scan_start_cb, .scan_ch_ready = scan_ch_ready_cb, .scan_complete = scan_complete_cb, .scan_issue_pbreq = scan_issue_pbreq_cb, .scan_issue_null_data = scan_issu_null_data_cb }; #ifdef CONFIG_P2P static struct rtw_phl_scan_ops scan_ops_p2p_cb = { .scan_start = scan_start_cb, .scan_ch_ready = scan_ch_ready_cb, .scan_complete = scan_complete_cb, .scan_issue_pbreq = scan_issue_p2p_pbreq_cb, .scan_issue_null_data = scan_issu_null_data_cb }; #endif #ifdef CONFIG_RTW_80211K static int scan_complete_rrm_cb(void *priv, struct rtw_phl_scan_param *param) { struct scan_priv *scan_priv = (struct scan_priv *)priv; _adapter *padapter = scan_priv->padapter; struct mlme_priv *pmlmepriv = &padapter->mlmepriv; struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv; int ret = _FAIL; if (!rtw_is_adapter_up(padapter)) goto _exit; mlmeext_set_scan_state(pmlmeext, SCAN_DISABLE); _rtw_spinlock_bh(&pmlmepriv->lock); _clr_fwstate_(pmlmepriv, WIFI_UNDER_SURVEY); _rtw_spinunlock_bh(&pmlmepriv->lock); /* inform RRM scan complete */ rm_post_event(padapter, scan_priv->rrm_token, RM_EV_survey_done); ret = _SUCCESS; _exit: RTW_INFO(FUNC_ADPT_FMT" takes %d ms to scan %d/%d channels\n", FUNC_ADPT_ARG(padapter), param->total_scan_time, param->ch_idx + 1, param->ch_num); _rtw_scan_abort_check(padapter, __func__); #ifdef CONFIG_CMD_SCAN _free_phl_param(padapter, param); pmlmeext->sitesurvey_res.scan_param = NULL; #else rtw_mfree(scan_priv, sizeof(*scan_priv)); #endif return ret; } static struct rtw_phl_scan_ops scan_ops_rrm_cb = { .scan_start = scan_start_cb, .scan_ch_ready = scan_ch_ready_cb, .scan_complete = scan_complete_rrm_cb, .scan_issue_pbreq = scan_issue_pbreq_cb, .scan_issue_null_data = scan_issu_null_data_cb }; #endif /* CONFIG_RTW_80211K */ #ifndef SCAN_PER_CH_EX_TIME #define SCAN_PER_CH_EX_TIME 40 /*8852bs sw ch ov*/ #endif /*SCAN_PER_CH_EX_TIME*/ static u32 rtw_scan_timeout_decision(_adapter *padapter) { u8 max_chan_num; u8 issue_null_time; u16 scan_ms; u16 p_ch_ex_time; u32 non_op_buf; u32 back_op_times = 0; struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv; struct ss_res *ss = &pmlmeext->sitesurvey_res; #ifdef CONFIG_SCAN_BACKOP u8 backop_cout = 0; #endif /*CONFIG_SCAN_BACKOP*/ if (padapter->registrypriv.scan_pch_ex_time != 0) p_ch_ex_time = padapter->registrypriv.scan_pch_ex_time; else p_ch_ex_time = SCAN_PER_CH_EX_TIME; /*issue null time,null(0)+null(1),undefine flag phl sleep 50ms*/ issue_null_time = 10; #ifndef RTW_WKARD_TX_NULL_WD_RP issue_null_time += 50; #endif /*RTW_WKARD_TX_NULL_WD_RP*/ if (is_supported_5g(padapter->registrypriv.band_type) && is_supported_24g(padapter->registrypriv.band_type)) max_chan_num = MAX_CHANNEL_NUM_2G_5G;/* dual band */ else max_chan_num = MAX_CHANNEL_NUM_2G;/*single band*/ #ifdef CONFIG_SCAN_BACKOP backop_cout = max_chan_num / ss->scan_cnt_max; /* delay 50ms to protect nulldata(1) */ if (rtw_scan_backop_decision(padapter)) back_op_times = backop_cout * (ss->backop_ms + p_ch_ex_time + issue_null_time); #endif if (ss->duration) scan_ms = ss->duration; else #if defined(CONFIG_RTW_ACS) && defined(CONFIG_RTW_ACS_DBG) if (IS_ACS_ENABLE(padapter) && rtw_is_acs_st_valid(padapter)) scan_ms = rtw_acs_get_adv_st(padapter); else #endif /*CONFIG_RTW_ACS*/ scan_ms = ss->scan_ch_ms; /*non op channel buffer time + scan start/done issue null*/ non_op_buf = max_chan_num * p_ch_ex_time + issue_null_time; ss->scan_timeout_ms = (scan_ms * max_chan_num) + back_op_times + non_op_buf; #ifdef DBG_SITESURVEY RTW_INFO("%s, max_chan_num(%d), p_ch_ex_time(%d), \ ss->scan_cnt_max=%d issue_null_time=%d\n" \ , __func__, max_chan_num, p_ch_ex_time, ss->scan_cnt_max, issue_null_time); RTW_INFO("%s , scan_timeout_ms = %d (ms), scan_ms=%d (ms), \ back_op_times=%d (ms), ss->duration=%d (ms)\n" \ , __func__, ss->scan_timeout_ms, scan_ms, back_op_times, ss->duration); #endif /*DBG_SITESURVEY*/ return ss->scan_timeout_ms; } /* rtw_sitesurvey_cmd(~) ### NOTE:#### (!!!!) MUST TAKE CARE THAT BEFORE CALLING THIS FUNC, YOU SHOULD HAVE LOCKED pmlmepriv->lock */ #ifdef CONFIG_CMD_SCAN static void scan_channel_list_filled(_adapter *padapter, struct rtw_phl_scan_param *phl_param, struct sitesurvey_parm *param) { struct phl_scan_channel *phl_ch = phl_param->ch; u8 i = 0; for (i = 0; i < param->ch_num; i++) { phl_ch[i].channel = param->ch[i].hw_value; phl_ch[i].scan_mode = NORMAL_SCAN_MODE; phl_ch[i].bw = param->bw; phl_ch[i].duration = param->duration; if (param->ch[i].flags & RTW_IEEE80211_CHAN_PASSIVE_SCAN) phl_ch[i].type = RTW_PHL_SCAN_PASSIVE; else phl_ch[i].type = RTW_PHL_SCAN_ACTIVE; } phl_param->ch_num = param->ch_num; } #ifdef RTW_WKARD_CMD_SCAN_EXTEND_ACTIVE_SCAN /* * Count extended active scan time(ms) and add time to * struct mlme_ext_priv.sitesurvey_res.scan_timeout_ms. * * Return extended active scan time which unit is ms. */ static u32 _scan_ext_act_time_count(struct _ADAPTER *a, struct rtw_phl_scan_param *scan) { struct ss_res *ss = &a->mlmeextpriv.sitesurvey_res; u16 ext_time = 0; int i; for (i = 0; i < scan->ch_num; i++) { if (scan->ch[i].ext_act_scan == EXT_ACT_SCAN_ENABLE) ext_time += scan->ext_act_scan_period; } #ifdef DBG_SITESURVEY RTW_PRINT(FUNC_ADPT_FMT ": Add extend active scan time %u ms to total " "scan time (from %u to %u)\n", FUNC_ADPT_ARG(a), ext_time, ss->scan_timeout_ms, ext_time + ss->scan_timeout_ms); #endif /* DBG_SITESURVEY */ ss->scan_timeout_ms += ext_time; return ext_time; } #endif /* RTW_WKARD_CMD_SCAN_EXTEND_ACTIVE_SCAN */ u8 rtw_sitesurvey_cmd(_adapter *padapter, struct sitesurvey_parm *pparm) { u8 res = _FAIL; u8 i; u32 scan_timeout_ms; struct mlme_priv *pmlmepriv = &padapter->mlmepriv; struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv; struct rtw_phl_scan_param *phl_param = NULL; struct rtw_ieee80211_channel ch[RTW_CHANNEL_SCAN_AMOUNT] = {0}; struct dvobj_priv *dvobj = adapter_to_dvobj(padapter); struct sitesurvey_parm *tmp_parm = NULL; struct mlme_ext_priv *mlmeext = &padapter->mlmeextpriv; struct ss_res *ss = &mlmeext->sitesurvey_res; struct rf_ctl_t *rfctl = adapter_to_rfctl(padapter); #ifdef CONFIG_RTW_80211K struct scan_priv *scan_priv = NULL; #endif if (pparm == NULL) { tmp_parm = rtw_zmalloc(sizeof(struct sitesurvey_parm)); if (tmp_parm == NULL) { RTW_ERR("%s alloc tmp_parm fail\n", __func__); goto _err_exit; } rtw_init_sitesurvey_parm(padapter, tmp_parm); pparm = tmp_parm; } /* backup original ch list */ _rtw_memcpy(ch, pparm->ch, sizeof(struct rtw_ieee80211_channel) * pparm->ch_num); /* modify ch list according to chanel plan */ pparm->ch_num = rtw_scan_ch_decision(padapter, pparm->ch, RTW_CHANNEL_SCAN_AMOUNT, ch, pparm->ch_num, pparm->acs); if (pparm->duration == 0) pparm->duration = ss->scan_ch_ms; /* ms */ /*create mem of PHL Scan parameter*/ phl_param = _alloc_phl_param(padapter, pparm->ch_num); if (phl_param == NULL) { RTW_ERR("%s alloc phl_param fail\n", __func__); goto _err_param; } /* STEP_1 transfer to rtw channel list to phl channel list */ scan_channel_list_filled(padapter, phl_param, pparm); /* STEP_2 copy the ssid info to phl param */ phl_param->ssid_num = rtw_min(pparm->ssid_num, SCAN_SSID_AMOUNT); for (i = 0; i < phl_param->ssid_num; ++i) { phl_param->ssid[i].ssid_len = pparm->ssid[i].SsidLength; _rtw_memcpy(&phl_param->ssid[i].ssid, &pparm->ssid[i].Ssid, phl_param->ssid[i].ssid_len); } #ifdef RTW_WKARD_CMD_SCAN_EXTEND_ACTIVE_SCAN /* STEP_2.1 set EXT_ACT_SCAN_ENABLE for hidden AP scan */ if (phl_param->ssid[0].ssid_len) { phl_param->ext_act_scan_period = RTW_EXTEND_ACTIVE_SCAN_PERIOD; for (i = 0; i < phl_param->ch_num; i++) { int chset_idx; chset_idx = rtw_chset_search_ch(rfctl->channel_set, phl_param->ch[i].channel); if (chset_idx < 0) { RTW_ERR(FUNC_ADPT_FMT ": cann't find ch %u in chset!\n", FUNC_ADPT_ARG(padapter), phl_param->ch[i].channel); continue; } if ((phl_param->ch[i].type == RTW_PHL_SCAN_PASSIVE) && (!IS_DFS_SLAVE_WITH_RD(rfctl) || rtw_rfctl_dfs_domain_unknown(rfctl) || !CH_IS_NON_OCP(&rfctl->channel_set[chset_idx]))) phl_param->ch[i].ext_act_scan = EXT_ACT_SCAN_ENABLE; } } #endif /* RTW_WKARD_CMD_SCAN_EXTEND_ACTIVE_SCAN */ /* STEP_3 set ops according to scan_type */ switch (pparm->scan_type) { #ifdef CONFIG_P2P case RTW_SCAN_P2P: phl_param->ops = &scan_ops_p2p_cb; break; #endif #ifdef CONFIG_RTW_80211K case RTW_SCAN_RRM: phl_param->ops = &scan_ops_rrm_cb; scan_priv = (struct scan_priv *)phl_param->priv; scan_priv->rrm_token = pparm->rrm_token; ss->token = pparm->rrm_token; break; #endif case RTW_SCAN_NORMAL: default: phl_param->ops = &scan_ops_cb; #ifdef CONFIG_SCAN_BACKOP if (rtw_scan_backop_decision(padapter)) { phl_param->back_op_ch_dur_ms = ss->backop_ms; phl_param->back_op_mode = SCAN_BKOP_CNT; phl_param->back_op_ch_cnt = ss->scan_cnt_max; } else { phl_param->back_op_mode = SCAN_BKOP_NONE; } #else phl_param->back_op_mode = SCAN_BKOP_NONE; #endif /* CONFIG_SCAN_BACKOP */ break; } /* STEP_4 reset variables for each scan */ for (i = 0; i < MAX_CHANNEL_NUM; i++) rfctl->channel_set[i].hidden_bss_cnt = 0; set_fwstate(pmlmepriv, WIFI_UNDER_SURVEY); if(rtw_phl_cmd_scan_request(dvobj->phl, phl_param, true) != RTW_PHL_STATUS_SUCCESS) { RTW_ERR("%s request scam_cmd failed\n", __func__); _clr_fwstate_(pmlmepriv, WIFI_UNDER_SURVEY); goto _err_req_param; } pmlmeext->sitesurvey_res.scan_param = phl_param; rtw_free_network_queue(padapter, _FALSE); pmlmepriv->scan_start_time = rtw_get_current_time(); scan_timeout_ms = rtw_scan_timeout_decision(padapter); #ifdef RTW_WKARD_CMD_SCAN_EXTEND_ACTIVE_SCAN scan_timeout_ms += _scan_ext_act_time_count(padapter, phl_param); #endif /* RTW_WKARD_CMD_SCAN_EXTEND_ACTIVE_SCAN */ mlme_set_scan_to_timer(pmlmepriv,scan_timeout_ms); rtw_led_control(padapter, LED_CTL_SITE_SURVEY); if (tmp_parm) rtw_mfree(tmp_parm, sizeof(*tmp_parm)); res = _SUCCESS; return res; _err_req_param: _free_phl_param(padapter, phl_param); _err_param: if (tmp_parm) rtw_mfree(tmp_parm, sizeof(*tmp_parm)); _err_exit: rtw_warn_on(1); return res; } #else /*!CONFIG_CMD_SCAN*/ /** * prepare phl_channel list according to SCAN type * */ static int scan_channel_list_preparation(_adapter *padapter, struct rtw_phl_scan_param *dst, struct sitesurvey_parm *src) { struct phl_scan_channel *phl_ch = NULL; int phl_ch_sz = 0; int i; phl_ch_sz = sizeof(struct phl_scan_channel) * (src->ch_num + 1); phl_ch = rtw_malloc(phl_ch_sz); if (phl_ch == NULL) { RTW_ERR("scan: alloc phl scan ch fail\n"); return -1; } _rtw_memset(phl_ch, 0, phl_ch_sz); i = 0; while (i < src->ch_num) { phl_ch[i].channel = src->ch[i].hw_value; phl_ch[i].scan_mode = NORMAL_SCAN_MODE; phl_ch[i].bw = src->bw; phl_ch[i].duration = src->duration; if (src->ch[i].flags & RTW_IEEE80211_CHAN_PASSIVE_SCAN) { phl_ch[i].type = RTW_PHL_SCAN_PASSIVE; } else { phl_ch[i].type = RTW_PHL_SCAN_ACTIVE; /* reduce scan time in active channel */ if (src->scan_type == RTW_SCAN_NORMAL) phl_ch[i].duration = src->duration >> 1; } i++; } dst->ch = phl_ch; dst->ch_sz = phl_ch_sz; dst->ch_num = src->ch_num; return 0; } u32 rtw_site_survey_fsm(_adapter *padapter, struct cmd_obj *pcmd) { u32 res = RTW_PHL_STATUS_FAILURE; struct scan_priv *scan_priv; struct rtw_phl_scan_param *phl_param; struct sitesurvey_parm *rtw_param; struct rtw_ieee80211_channel ch[RTW_CHANNEL_SCAN_AMOUNT]; u8 i; scan_priv = rtw_malloc(sizeof(*scan_priv)); if (scan_priv == NULL) { RTW_ERR("scan: %s alloc scan_priv fail\n", __func__); return RTW_PHL_STATUS_FAILURE; } _rtw_memset(scan_priv, 0, sizeof(*scan_priv)); scan_priv->padapter = padapter; rtw_param = (struct sitesurvey_parm *)pcmd->parmbuf; if (rtw_param->duration == 0) rtw_param->duration = SURVEY_TO; /* ms */ /* backup original ch list */ _rtw_memcpy(ch, rtw_param->ch, sizeof(struct rtw_ieee80211_channel) * rtw_param->ch_num); /* modify ch list according to chanel plan */ rtw_param->ch_num = rtw_scan_ch_decision(padapter, rtw_param->ch, RTW_CHANNEL_SCAN_AMOUNT, ch, rtw_param->ch_num, rtw_param->acs); phl_param = rtw_malloc(sizeof(*phl_param)); if (phl_param == NULL) { RTW_ERR("scan: %s alloc param fail\n", __func__); if (scan_priv) rtw_mfree(scan_priv, sizeof(*scan_priv)); return RTW_PHL_STATUS_FAILURE; } _rtw_memset(phl_param, 0, sizeof(*phl_param)); /* transfer to rtw channel list to phl channel list */ scan_channel_list_preparation(padapter, phl_param, rtw_param); /* copy the ssid info to phl param */ phl_param->ssid_num = rtw_min(rtw_param->ssid_num, SCAN_SSID_AMOUNT); for (i = 0; i < phl_param->ssid_num; ++i) { phl_param->ssid[i].ssid_len = rtw_param->ssid[i].SsidLength; _rtw_memcpy(&phl_param->ssid[i].ssid, &rtw_param->ssid[i].Ssid, phl_param->ssid[i].ssid_len); } switch (rtw_param->scan_type) { #ifdef CONFIG_P2P case RTW_SCAN_P2P: phl_param->ops = &scan_ops_p2p_cb; break; #endif #ifdef CONFIG_RTW_80211K case RTW_SCAN_RRM: phl_param->ops = &scan_ops_rrm_cb; if (rtw_param->ch_num > 13) { phl_param->back_op_mode = SCAN_BKOP_CNT; phl_param->back_op_ch_cnt = 3; } break; #endif case RTW_SCAN_NORMAL: default: phl_param->ops = &scan_ops_cb; phl_param->back_op_mode = SCAN_BKOP_CNT; phl_param->back_op_ch_cnt = 3; break; } phl_param->priv = scan_priv; phl_param->wifi_role = padapter->phl_role; res = rtw_phl_scan_request(adapter_to_dvobj(padapter)->phl, phl_param, TO_TAIL); rtw_mfree(phl_param->ch, phl_param->ch_sz); rtw_mfree(phl_param, sizeof(*phl_param)); return res; } u8 rtw_sitesurvey_cmd(_adapter *padapter, struct sitesurvey_parm *pparm) { u8 res = _FAIL; struct cmd_obj *cmd; struct sitesurvey_parm *psurveyPara; struct cmd_priv *pcmdpriv = &adapter_to_dvobj(padapter)->cmdpriv; struct mlme_priv *pmlmepriv = &padapter->mlmepriv; #ifdef CONFIG_LPS if (check_fwstate(pmlmepriv, WIFI_ASOC_STATE) == _TRUE) rtw_lps_ctrl_wk_cmd(padapter, LPS_CTRL_SCAN, 0); #endif #ifdef CONFIG_P2P_PS if (check_fwstate(pmlmepriv, WIFI_ASOC_STATE) == _TRUE) p2p_ps_wk_cmd(padapter, P2P_PS_SCAN, 1); #endif /* CONFIG_P2P_PS */ cmd = (struct cmd_obj *)rtw_zmalloc(sizeof(struct cmd_obj)); if (cmd == NULL) return _FAIL; cmd->padapter = padapter; psurveyPara = (struct sitesurvey_parm *)rtw_zmalloc(sizeof(struct sitesurvey_parm)); if (psurveyPara == NULL) { rtw_mfree((unsigned char *) cmd, sizeof(struct cmd_obj)); return _FAIL; } if (pparm) _rtw_memcpy(psurveyPara, pparm, sizeof(struct sitesurvey_parm)); else psurveyPara->scan_mode = pmlmepriv->scan_mode; rtw_free_network_queue(padapter, _FALSE); init_h2fwcmd_w_parm_no_rsp(cmd, psurveyPara, CMD_SITE_SURVEY); set_fwstate(pmlmepriv, WIFI_UNDER_SURVEY); res = rtw_enqueue_cmd(pcmdpriv, cmd); if (res == _SUCCESS) { u32 scan_timeout_ms; pmlmepriv->scan_start_time = rtw_get_current_time(); scan_timeout_ms = rtw_scan_timeout_decision(padapter); mlme_set_scan_to_timer(pmlmepriv,scan_timeout_ms); rtw_led_control(padapter, LED_CTL_SITE_SURVEY); } else { _clr_fwstate_(pmlmepriv, WIFI_UNDER_SURVEY); } return res; } #endif/*CONFIG_CMD_SCAN*/ /* inform caller phl_scan are ready on remain channel */ static int roch_ready_cb(void *priv, struct rtw_phl_scan_param *param) { struct scan_priv *scan_priv = (struct scan_priv *)priv; _adapter *padapter = scan_priv->padapter; struct cfg80211_roch_info *pcfg80211_rochinfo = &padapter->cfg80211_rochinfo; RTW_INFO("%s cookie:0x%llx\n", __func__, pcfg80211_rochinfo->remain_on_ch_cookie); if ((scan_priv->roch_step & ROCH_CH_READY)) return 0; scan_priv->roch_step |= ROCH_CH_READY; rtw_cfg80211_ready_on_channel( scan_priv->wdev, scan_priv->cookie, &scan_priv->channel, scan_priv->channel_type, scan_priv->duration, GFP_KERNEL); return 0; } static int roch_off_ch_tx_cb(void *priv, struct rtw_phl_scan_param *param, void *data) { struct scan_priv *scan_priv = (struct scan_priv *)priv; struct dvobj_priv *dvobj = adapter_to_dvobj(scan_priv->padapter); #ifdef CONFIG_CMD_SCAN RTW_ERR("CMD_SCAN call %s\n", __func__); rtw_warn_on(1); #else phl_cmd_complete_job(dvobj->phl, (struct phl_cmd_job *)data); #endif return 0; } #ifdef CONFIG_P2P static int p2p_roch_complete_cb(void *priv, struct rtw_phl_scan_param *param) { struct scan_priv *scan_priv = (struct scan_priv *)priv; _adapter *padapter = scan_priv->padapter; int ret = _FAIL; struct rtw_wdev_priv *pwdev_priv = adapter_wdev_data(padapter); struct cfg80211_roch_info *pcfg80211_rochinfo = &padapter->cfg80211_rochinfo; struct wifidirect_info *pwdinfo = &padapter->wdinfo; if (!rtw_is_adapter_up(padapter)) goto _exit; mlmeext_set_scan_state(&padapter->mlmeextpriv, SCAN_DISABLE); /* roch_ready() and roch_complete() MUST be a PAIR * otherwise will caurse wpa_supplicant hang!!! * This case may happen when someone cancel remain on ch * before it really start. (called roch_ready()). */ if (!(scan_priv->roch_step & ROCH_CH_READY)) roch_ready_cb(priv, param); #ifndef CONFIG_CMD_SCAN rtw_back_opch(padapter); #endif #ifdef CONFIG_DEBUG_CFG80211 RTW_INFO("%s, role=%d\n", __func__, rtw_p2p_role(pwdinfo)); #endif rtw_cfg80211_set_is_roch(padapter, _FALSE); pcfg80211_rochinfo->ro_ch_wdev = NULL; rtw_cfg80211_set_last_ro_ch_time(padapter); ret = _SUCCESS; _exit: /* callback to cfg80211 */ rtw_cfg80211_remain_on_channel_expired(scan_priv->wdev , scan_priv->cookie , &scan_priv->channel , scan_priv->channel_type, GFP_KERNEL); RTW_INFO("cfg80211_remain_on_channel_expired cookie:0x%llx\n" , pcfg80211_rochinfo->remain_on_ch_cookie); RTW_INFO(FUNC_ADPT_FMT" takes %d ms to scan %d/%d channels\n", FUNC_ADPT_ARG(padapter), param->total_scan_time, #ifdef CONFIG_CMD_SCAN param->ch_idx, #else param->ch_idx + 1, #endif param->ch_num); _rtw_scan_abort_check(padapter, __func__); #ifdef CONFIG_CMD_SCAN _free_phl_param(padapter, param); padapter->mlmeextpriv.sitesurvey_res.scan_param = NULL; #else rtw_mfree(scan_priv, sizeof(*scan_priv)); #endif return ret; } static int p2p_roch_start_cb(void *priv, struct rtw_phl_scan_param *param) { struct scan_priv *scan_priv = (struct scan_priv *)priv; _adapter *padapter = scan_priv->padapter; struct cfg80211_roch_info *pcfg80211_rochinfo; pcfg80211_rochinfo = &padapter->cfg80211_rochinfo; //TODO remove mlmeext_set_scan_state(&padapter->mlmeextpriv, SCAN_PROCESS); rtw_cfg80211_set_is_roch(padapter, _TRUE); pcfg80211_rochinfo->ro_ch_wdev = scan_priv->wdev; pcfg80211_rochinfo->remain_on_ch_cookie = scan_priv->cookie; pcfg80211_rochinfo->duration = scan_priv->duration; rtw_cfg80211_set_last_ro_ch_time(padapter); _rtw_memcpy(&pcfg80211_rochinfo->remain_on_ch_channel, &scan_priv->channel, sizeof(struct ieee80211_channel)); #if (KERNEL_VERSION(3, 8, 0) > LINUX_VERSION_CODE) pcfg80211_rochinfo->remain_on_ch_type = scan_priv->channel_type; #endif pcfg80211_rochinfo->restore_channel = scan_priv->restore_ch; #ifdef CONFIG_CMD_SCAN padapter->mlmeextpriv.sitesurvey_res.scan_param = param; #endif return 0; } #endif static int roch_start_cb(void *priv, struct rtw_phl_scan_param *param) { struct scan_priv *scan_priv = (struct scan_priv *)priv; _adapter *padapter = scan_priv->padapter; mlmeext_set_scan_state(&padapter->mlmeextpriv, SCAN_PROCESS); rtw_cfg80211_set_is_roch(padapter, _TRUE); #ifdef CONFIG_CMD_SCAN padapter->mlmeextpriv.sitesurvey_res.scan_param = param; #endif return 0; } static int roch_complete_cb(void *priv, struct rtw_phl_scan_param *param) { struct scan_priv *scan_priv = (struct scan_priv *)priv; _adapter *padapter = scan_priv->padapter; struct cfg80211_roch_info *pcfg80211_rochinfo = &padapter->cfg80211_rochinfo; struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv; int ret = _FAIL; if (!rtw_is_adapter_up(padapter)) goto _exit; mlmeext_set_scan_state(pmlmeext, SCAN_DISABLE); /* roch_ready() and roch_complete() MUST be a PAIR * otherwise will caurse wpa_supplicant hang!!! * This case may happen when someone cancel remain on ch * before it really start. (called roch_ready()). */ if (!(scan_priv->roch_step & ROCH_CH_READY)) roch_ready_cb(priv, param); rtw_cfg80211_set_is_roch(padapter, _FALSE); ret = _SUCCESS; _exit: /* callback to cfg80211 */ rtw_cfg80211_remain_on_channel_expired(scan_priv->wdev , scan_priv->cookie , &scan_priv->channel , scan_priv->channel_type, GFP_KERNEL); RTW_INFO("cfg80211_remain_on_channel_expired cookie:0x%llx\n" , pcfg80211_rochinfo->remain_on_ch_cookie); RTW_INFO(FUNC_ADPT_FMT" takes %d ms to scan %d/%d channels\n", FUNC_ADPT_ARG(padapter), param->total_scan_time, #ifdef CONFIG_CMD_SCAN param->ch_idx, #else param->ch_idx + 1, #endif param->ch_num); _rtw_scan_abort_check(padapter, __func__); #ifdef CONFIG_CMD_SCAN _free_phl_param(padapter, param); pmlmeext->sitesurvey_res.scan_param = NULL; #else rtw_mfree(scan_priv, sizeof(*scan_priv)); #endif return ret; } #ifdef CONFIG_P2P /* p2p remain on channel */ static struct rtw_phl_scan_ops p2p_remain_ops_cb = { .scan_start = p2p_roch_start_cb, .scan_ch_ready = roch_ready_cb, .scan_off_ch_tx = roch_off_ch_tx_cb, .scan_complete = p2p_roch_complete_cb, .scan_issue_null_data = scan_issu_null_data_cb }; #endif /* normal remain on channel */ static struct rtw_phl_scan_ops remain_ops_cb = { .scan_start = roch_start_cb, .scan_ch_ready = roch_ready_cb, .scan_off_ch_tx = roch_off_ch_tx_cb, .scan_complete = roch_complete_cb, .scan_issue_null_data = scan_issu_null_data_cb }; #ifdef CONFIG_IOCTL_CFG80211 static u8 roch_stay_in_cur_chan(_adapter *padapter) { int i; _adapter *iface; struct mlme_priv *pmlmepriv; struct dvobj_priv *dvobj = adapter_to_dvobj(padapter); u8 rst = _FALSE; for (i = 0; i < dvobj->iface_nums; i++) { iface = dvobj->padapters[i]; if (iface) { pmlmepriv = &iface->mlmepriv; if (check_fwstate(pmlmepriv, WIFI_UNDER_LINKING | WIFI_UNDER_WPS | WIFI_UNDER_KEY_HANDSHAKE) == _TRUE) { RTW_INFO(ADPT_FMT"- WIFI_UNDER_LINKING |WIFI_UNDER_WPS | WIFI_UNDER_KEY_HANDSHAKE (mlme state:0x%x)\n", ADPT_ARG(iface), get_fwstate(&iface->mlmepriv)); rst = _TRUE; break; } #ifdef CONFIG_AP_MODE if (MLME_IS_AP(iface) || MLME_IS_MESH(iface)) { if (rtw_ap_sta_states_check(iface) == _TRUE) { rst = _TRUE; break; } } #endif } } return rst; } #ifdef CONFIG_CMD_SCAN u8 rtw_phl_remain_on_ch_cmd(_adapter *padapter, u64 cookie, struct wireless_dev *wdev, struct ieee80211_channel *ch, u8 ch_type, unsigned int duration, struct back_op_param *bkop_parm, u8 is_p2p) { struct dvobj_priv *dvobj = adapter_to_dvobj(padapter); struct rtw_phl_scan_param *phl_param = NULL; struct scan_priv *scan_priv = NULL; u16 remain_ch; u8 chan_num; u8 res = _FAIL; /* prepare remain channel - check channel */ remain_ch = (u16)ieee80211_frequency_to_channel(ch->center_freq); if (roch_stay_in_cur_chan(padapter) == _TRUE) { /*???*/ remain_ch = rtw_mi_get_union_chan(padapter); RTW_INFO(FUNC_ADPT_FMT" stay in union ch:%d\n", FUNC_ADPT_ARG(padapter), remain_ch); } chan_num = 1; phl_param = _alloc_phl_param(padapter, chan_num); if (phl_param == NULL) { RTW_ERR("%s alloc phl_param fail\n", __func__); goto _err_exit; } /*** fill phl parameter - scan_priv ***/ scan_priv = (struct scan_priv *)phl_param->priv; scan_priv->padapter = padapter; scan_priv->wdev = wdev; _rtw_memcpy(&scan_priv->channel, ch, sizeof(*ch)); scan_priv->channel_type = ch_type; scan_priv->cookie = cookie; scan_priv->duration = duration; scan_priv->restore_ch = rtw_get_oper_ch(padapter); /* fill phl param - chan */ phl_param->ch->channel = remain_ch; phl_param->ch->duration = duration; phl_param->ch->scan_mode = P2P_LISTEN_MODE; phl_param->ch->bw = CHANNEL_WIDTH_20; phl_param->ch_num = chan_num; /* fill back op param */ phl_param->back_op_mode = SCAN_BKOP_TIMER; phl_param->back_op_ch_cnt = 1; phl_param->back_op_ch_dur_ms = bkop_parm->on_ch_dur;/*op_ch time*/ phl_param->back_op_off_ch_dur_ms = bkop_parm->off_ch_dur;/*ro_ch time*/ phl_param->back_op_off_ch_ext_dur_ms = bkop_parm->off_ch_ext_dur; #ifdef CONFIG_P2P /* set ops according to is_p2p */ if (is_p2p) phl_param->ops = &p2p_remain_ops_cb; else #endif phl_param->ops = &remain_ops_cb; if(rtw_phl_cmd_scan_request(dvobj->phl, phl_param, true) == RTW_PHL_STATUS_FAILURE) { RTW_ERR("%s request scam_cmd failed\n", __func__); goto _err_req_param; } RTW_INFO(FUNC_ADPT_FMT" ch:%u duration:%d, cookie:0x%llx\n" , FUNC_ADPT_ARG(padapter), remain_ch, duration, cookie); res = _SUCCESS; return res; _err_req_param: _free_phl_param(padapter, phl_param); _err_exit: rtw_warn_on(1); return res; } #else u8 rtw_phl_remain_on_ch_cmd(_adapter *padapter, u64 cookie, struct wireless_dev *wdev, struct ieee80211_channel *ch, u8 ch_type, unsigned int duration, struct back_op_param *bkop_parm, u8 is_p2p) { struct dvobj_priv *dvobj = adapter_to_dvobj(padapter); struct rtw_phl_scan_param phl_param; struct scan_priv *scan_priv; struct phl_scan_channel phl_ch; int phl_ch_sz = 0; u16 remain_ch; u8 res = _FAIL; _rtw_memset(&phl_param, 0, sizeof(phl_param)); scan_priv = rtw_malloc(sizeof(*scan_priv)); if (scan_priv == NULL) { RTW_ERR("scan: %s alloc scan_priv fail\n", __func__); return res; } _rtw_memset(scan_priv, 0, sizeof(*scan_priv)); scan_priv->padapter = padapter; scan_priv->wdev = wdev; _rtw_memcpy(&scan_priv->channel, ch, sizeof(*ch)); scan_priv->channel_type = ch_type; scan_priv->cookie = cookie; scan_priv->duration = duration; scan_priv->restore_ch = rtw_get_oper_ch(padapter); phl_param.priv = scan_priv; /* check channel */ remain_ch = (u16)ieee80211_frequency_to_channel(ch->center_freq); if (roch_stay_in_cur_chan(padapter) == _TRUE) { remain_ch = rtw_mi_get_union_chan(padapter); RTW_INFO(FUNC_ADPT_FMT" stay in union ch:%d\n", FUNC_ADPT_ARG(padapter), remain_ch); } /* prepare remain channel */ phl_ch_sz = sizeof(struct phl_scan_channel); _rtw_memset(&phl_ch, 0, phl_ch_sz); phl_ch.channel = remain_ch; phl_ch.duration = scan_priv->duration; phl_ch.scan_mode = NORMAL_SCAN_MODE; phl_ch.bw = CHANNEL_WIDTH_20; phl_param.ch = &phl_ch; phl_param.ch_sz = phl_ch_sz; phl_param.ch_num = 1; phl_param.wifi_role = padapter->phl_role; phl_param.back_op_mode = SCAN_BKOP_TIMER; phl_param.back_op_ch_dur_ms = bkop_parm->on_ch_dur; phl_param.back_op_off_ch_dur_ms = bkop_parm->off_ch_dur; phl_param.back_op_off_ch_ext_dur_ms = bkop_parm->off_ch_ext_dur; #ifdef CONFIG_P2P if (is_p2p) phl_param.ops = &p2p_remain_ops_cb; else #endif phl_param.ops = &remain_ops_cb; RTW_INFO(FUNC_ADPT_FMT" ch:%u duration:%d, cookie:0x%llx\n" , FUNC_ADPT_ARG(padapter), phl_ch.channel, scan_priv->duration, cookie); /* sent message to request phl scan * IMMEDIATE imply cancelling previous scan request if has */ rtw_phl_scan_request(dvobj->phl, &phl_param, IMMEDIATE); /* scan_priv will be cancelled in roch_complete_cb */ res = _SUCCESS; return res; } #endif #endif /*CONFIG_IOCTL_CFG80211*/ #endif /*CONFIG_PHL_ARCH*/