| // SPDX-License-Identifier: GPL-2.0 |
| /****************************************************************************** |
| * |
| * Copyright(c) 2007 - 2012 Realtek Corporation. All rights reserved. |
| * |
| ******************************************************************************/ |
| #define _RTW_MLME_EXT_C_ |
| |
| #include <linux/ieee80211.h> |
| #include <asm/unaligned.h> |
| |
| #include <osdep_service.h> |
| #include <drv_types.h> |
| #include <wifi.h> |
| #include <rtw_mlme_ext.h> |
| #include <wlan_bssdef.h> |
| #include <mlme_osdep.h> |
| #include <recv_osdep.h> |
| |
| static u8 null_addr[ETH_ALEN] = {}; |
| |
| /************************************************** |
| OUI definitions for the vendor specific IE |
| ***************************************************/ |
| const u8 RTW_WPA_OUI[] = {0x00, 0x50, 0xf2, 0x01}; |
| const u8 WPS_OUI[] = {0x00, 0x50, 0xf2, 0x04}; |
| static const u8 WMM_OUI[] = {0x00, 0x50, 0xf2, 0x02}; |
| static const u8 P2P_OUI[] = {0x50, 0x6F, 0x9A, 0x09}; |
| |
| static const u8 WMM_PARA_OUI[] = {0x00, 0x50, 0xf2, 0x02, 0x01, 0x01}; |
| |
| const u8 WPA_TKIP_CIPHER[4] = {0x00, 0x50, 0xf2, 0x02}; |
| const u8 RSN_TKIP_CIPHER[4] = {0x00, 0x0f, 0xac, 0x02}; |
| |
| /******************************************************** |
| MCS rate definitions |
| *********************************************************/ |
| const u8 MCS_rate_1R[16] = { |
| 0xff, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 |
| }; |
| |
| /******************************************************** |
| ChannelPlan definitions |
| *********************************************************/ |
| static struct rt_channel_plan_2g RTW_ChannelPlan2G[RT_CHANNEL_DOMAIN_2G_MAX] = { |
| {{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}, 13}, /* 0x00, RT_CHANNEL_DOMAIN_2G_WORLD , Passive scan CH 12, 13 */ |
| {{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}, 13}, /* 0x01, RT_CHANNEL_DOMAIN_2G_ETSI1 */ |
| {{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, 11}, /* 0x02, RT_CHANNEL_DOMAIN_2G_FCC1 */ |
| {{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}, 14}, /* 0x03, RT_CHANNEL_DOMAIN_2G_MIKK1 */ |
| {{10, 11, 12, 13}, 4}, /* 0x04, RT_CHANNEL_DOMAIN_2G_ETSI2 */ |
| {{}, 0}, /* 0x05, RT_CHANNEL_DOMAIN_2G_NULL */ |
| }; |
| |
| static struct rt_channel_plan_map RTW_ChannelPlanMap[RT_CHANNEL_DOMAIN_MAX] = { |
| /* 0x00 ~ 0x1F , Old Define ===== */ |
| {0x02}, /* 0x00, RT_CHANNEL_DOMAIN_FCC */ |
| {0x02}, /* 0x01, RT_CHANNEL_DOMAIN_IC */ |
| {0x01}, /* 0x02, RT_CHANNEL_DOMAIN_ETSI */ |
| {0x01}, /* 0x03, RT_CHANNEL_DOMAIN_SPAIN */ |
| {0x01}, /* 0x04, RT_CHANNEL_DOMAIN_FRANCE */ |
| {0x03}, /* 0x05, RT_CHANNEL_DOMAIN_MKK */ |
| {0x03}, /* 0x06, RT_CHANNEL_DOMAIN_MKK1 */ |
| {0x01}, /* 0x07, RT_CHANNEL_DOMAIN_ISRAEL */ |
| {0x03}, /* 0x08, RT_CHANNEL_DOMAIN_TELEC */ |
| {0x03}, /* 0x09, RT_CHANNEL_DOMAIN_GLOBAL_DOAMIN */ |
| {0x00}, /* 0x0A, RT_CHANNEL_DOMAIN_WORLD_WIDE_13 */ |
| {0x02}, /* 0x0B, RT_CHANNEL_DOMAIN_TAIWAN */ |
| {0x01}, /* 0x0C, RT_CHANNEL_DOMAIN_CHINA */ |
| {0x02}, /* 0x0D, RT_CHANNEL_DOMAIN_SINGAPORE_INDIA_MEXICO */ |
| {0x02}, /* 0x0E, RT_CHANNEL_DOMAIN_KOREA */ |
| {0x02}, /* 0x0F, RT_CHANNEL_DOMAIN_TURKEY */ |
| {0x01}, /* 0x10, RT_CHANNEL_DOMAIN_JAPAN */ |
| {0x02}, /* 0x11, RT_CHANNEL_DOMAIN_FCC_NO_DFS */ |
| {0x01}, /* 0x12, RT_CHANNEL_DOMAIN_JAPAN_NO_DFS */ |
| {0x00}, /* 0x13, RT_CHANNEL_DOMAIN_WORLD_WIDE_5G */ |
| {0x02}, /* 0x14, RT_CHANNEL_DOMAIN_TAIWAN_NO_DFS */ |
| {0x00}, /* 0x15, RT_CHANNEL_DOMAIN_ETSI_NO_DFS */ |
| {0x00}, /* 0x16, RT_CHANNEL_DOMAIN_KOREA_NO_DFS */ |
| {0x03}, /* 0x17, RT_CHANNEL_DOMAIN_JAPAN_NO_DFS */ |
| {0x05}, /* 0x18, RT_CHANNEL_DOMAIN_PAKISTAN_NO_DFS */ |
| {0x02}, /* 0x19, RT_CHANNEL_DOMAIN_TAIWAN2_NO_DFS */ |
| {0x00}, /* 0x1A, */ |
| {0x00}, /* 0x1B, */ |
| {0x00}, /* 0x1C, */ |
| {0x00}, /* 0x1D, */ |
| {0x00}, /* 0x1E, */ |
| {0x05}, /* 0x1F, RT_CHANNEL_DOMAIN_WORLD_WIDE_ONLY_5G */ |
| /* 0x20 ~ 0x7F , New Define ===== */ |
| {0x00}, /* 0x20, RT_CHANNEL_DOMAIN_WORLD_NULL */ |
| {0x01}, /* 0x21, RT_CHANNEL_DOMAIN_ETSI1_NULL */ |
| {0x02}, /* 0x22, RT_CHANNEL_DOMAIN_FCC1_NULL */ |
| {0x03}, /* 0x23, RT_CHANNEL_DOMAIN_MKK1_NULL */ |
| {0x04}, /* 0x24, RT_CHANNEL_DOMAIN_ETSI2_NULL */ |
| {0x02}, /* 0x25, RT_CHANNEL_DOMAIN_FCC1_FCC1 */ |
| {0x00}, /* 0x26, RT_CHANNEL_DOMAIN_WORLD_ETSI1 */ |
| {0x03}, /* 0x27, RT_CHANNEL_DOMAIN_MKK1_MKK1 */ |
| {0x00}, /* 0x28, RT_CHANNEL_DOMAIN_WORLD_KCC1 */ |
| {0x00}, /* 0x29, RT_CHANNEL_DOMAIN_WORLD_FCC2 */ |
| {0x00}, /* 0x2A, */ |
| {0x00}, /* 0x2B, */ |
| {0x00}, /* 0x2C, */ |
| {0x00}, /* 0x2D, */ |
| {0x00}, /* 0x2E, */ |
| {0x00}, /* 0x2F, */ |
| {0x00}, /* 0x30, RT_CHANNEL_DOMAIN_WORLD_FCC3 */ |
| {0x00}, /* 0x31, RT_CHANNEL_DOMAIN_WORLD_FCC4 */ |
| {0x00}, /* 0x32, RT_CHANNEL_DOMAIN_WORLD_FCC5 */ |
| {0x00}, /* 0x33, RT_CHANNEL_DOMAIN_WORLD_FCC6 */ |
| {0x02}, /* 0x34, RT_CHANNEL_DOMAIN_FCC1_FCC7 */ |
| {0x00}, /* 0x35, RT_CHANNEL_DOMAIN_WORLD_ETSI2 */ |
| {0x00}, /* 0x36, RT_CHANNEL_DOMAIN_WORLD_ETSI3 */ |
| {0x03}, /* 0x37, RT_CHANNEL_DOMAIN_MKK1_MKK2 */ |
| {0x03}, /* 0x38, RT_CHANNEL_DOMAIN_MKK1_MKK3 */ |
| {0x02}, /* 0x39, RT_CHANNEL_DOMAIN_FCC1_NCC1 */ |
| {0x00}, /* 0x3A, */ |
| {0x00}, /* 0x3B, */ |
| {0x00}, /* 0x3C, */ |
| {0x00}, /* 0x3D, */ |
| {0x00}, /* 0x3E, */ |
| {0x00}, /* 0x3F, */ |
| {0x02}, /* 0x40, RT_CHANNEL_DOMAIN_FCC1_NCC2 */ |
| {0x03}, /* 0x41, RT_CHANNEL_DOMAIN_GLOBAL_DOAMIN_2G */ |
| }; |
| |
| static const struct rt_channel_plan_map RTW_CHANNEL_PLAN_MAP_REALTEK_DEFINE = { |
| 0x03 |
| }; /* use the combination for max channel numbers */ |
| |
| /* |
| * Search the @param channel_num in given @param channel_set |
| * @ch_set: the given channel set |
| * @ch: the given channel number |
| * |
| * return the index of channel_num in channel_set, -1 if not found |
| */ |
| int rtw_ch_set_search_ch(struct rt_channel_info *ch_set, const u32 ch) |
| { |
| int i; |
| |
| for (i = 0; ch_set[i].ChannelNum != 0; i++) { |
| if (ch == ch_set[i].ChannelNum) |
| break; |
| } |
| |
| if (i >= ch_set[i].ChannelNum) |
| return -1; |
| return i; |
| } |
| |
| struct xmit_frame *alloc_mgtxmitframe(struct xmit_priv *pxmitpriv) |
| { |
| struct xmit_frame *pmgntframe; |
| struct xmit_buf *pxmitbuf; |
| |
| pmgntframe = rtw_alloc_xmitframe(pxmitpriv); |
| if (!pmgntframe) { |
| DBG_88E("%s, alloc xmitframe fail\n", __func__); |
| return NULL; |
| } |
| |
| pxmitbuf = rtw_alloc_xmitbuf_ext(pxmitpriv); |
| if (!pxmitbuf) { |
| DBG_88E("%s, alloc xmitbuf fail\n", __func__); |
| rtw_free_xmitframe(pxmitpriv, pmgntframe); |
| return NULL; |
| } |
| pmgntframe->frame_tag = MGNT_FRAMETAG; |
| pmgntframe->pxmitbuf = pxmitbuf; |
| pmgntframe->buf_addr = pxmitbuf->pbuf; |
| pxmitbuf->priv_data = pmgntframe; |
| return pmgntframe; |
| } |
| |
| /**************************************************************************** |
| |
| Following are some TX functions for WiFi MLME |
| |
| *****************************************************************************/ |
| |
| void update_mgnt_tx_rate(struct adapter *padapter, u8 rate) |
| { |
| struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv; |
| |
| pmlmeext->tx_rate = rate; |
| DBG_88E("%s(): rate = %x\n", __func__, rate); |
| } |
| |
| void update_mgntframe_attrib(struct adapter *padapter, struct pkt_attrib *pattrib) |
| { |
| struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv; |
| |
| memset((u8 *)(pattrib), 0, sizeof(struct pkt_attrib)); |
| |
| pattrib->hdrlen = 24; |
| pattrib->nr_frags = 1; |
| pattrib->priority = 7; |
| pattrib->mac_id = 0; |
| pattrib->qsel = 0x12; |
| |
| pattrib->pktlen = 0; |
| |
| if (pmlmeext->cur_wireless_mode & WIRELESS_11B) |
| pattrib->raid = 6;/* b mode */ |
| else |
| pattrib->raid = 5;/* a/g mode */ |
| |
| pattrib->encrypt = _NO_PRIVACY_; |
| pattrib->bswenc = false; |
| |
| pattrib->qos_en = false; |
| pattrib->ht_en = false; |
| pattrib->bwmode = HT_CHANNEL_WIDTH_20; |
| pattrib->ch_offset = HAL_PRIME_CHNL_OFFSET_DONT_CARE; |
| pattrib->sgi = false; |
| |
| pattrib->seqnum = pmlmeext->mgnt_seq; |
| |
| pattrib->retry_ctrl = true; |
| } |
| |
| static void dump_mgntframe(struct adapter *padapter, |
| struct xmit_frame *pmgntframe) |
| { |
| if (padapter->bSurpriseRemoved || padapter->bDriverStopped) |
| return; |
| |
| rtw_hal_mgnt_xmit(padapter, pmgntframe); |
| } |
| |
| static s32 dump_mgntframe_and_wait(struct adapter *padapter, |
| struct xmit_frame *pmgntframe, |
| int timeout_ms) |
| { |
| s32 ret = _FAIL; |
| struct xmit_buf *pxmitbuf = pmgntframe->pxmitbuf; |
| struct submit_ctx sctx; |
| |
| if (padapter->bSurpriseRemoved || padapter->bDriverStopped) |
| return ret; |
| |
| rtw_sctx_init(&sctx, timeout_ms); |
| pxmitbuf->sctx = &sctx; |
| |
| ret = rtw_hal_mgnt_xmit(padapter, pmgntframe); |
| |
| if (ret == _SUCCESS) |
| ret = rtw_sctx_wait(&sctx); |
| |
| return ret; |
| } |
| |
| static s32 dump_mgntframe_and_wait_ack(struct adapter *padapter, |
| struct xmit_frame *pmgntframe) |
| { |
| s32 ret = _FAIL; |
| u32 timeout_ms = 500;/* 500ms */ |
| struct xmit_priv *pxmitpriv = &padapter->xmitpriv; |
| |
| if (padapter->bSurpriseRemoved || padapter->bDriverStopped) |
| return -1; |
| |
| if (mutex_lock_interruptible(&pxmitpriv->ack_tx_mutex)) |
| return _FAIL; |
| pxmitpriv->ack_tx = true; |
| |
| pmgntframe->ack_report = 1; |
| if (rtw_hal_mgnt_xmit(padapter, pmgntframe) == _SUCCESS) |
| ret = rtw_ack_tx_wait(pxmitpriv, timeout_ms); |
| |
| pxmitpriv->ack_tx = false; |
| mutex_unlock(&pxmitpriv->ack_tx_mutex); |
| |
| return ret; |
| } |
| |
| static int update_hidden_ssid(u8 *ies, u32 ies_len, u8 hidden_ssid_mode) |
| { |
| u8 *ssid_ie; |
| uint ssid_len_ori; |
| int len_diff = 0; |
| |
| ssid_ie = rtw_get_ie(ies, WLAN_EID_SSID, &ssid_len_ori, ies_len); |
| |
| if (ssid_ie && ssid_len_ori > 0) { |
| switch (hidden_ssid_mode) { |
| case 1: { |
| u8 *next_ie = ssid_ie + 2 + ssid_len_ori; |
| u32 remain_len = 0; |
| |
| remain_len = ies_len - (next_ie - ies); |
| |
| ssid_ie[1] = 0; |
| memcpy(ssid_ie+2, next_ie, remain_len); |
| len_diff -= ssid_len_ori; |
| |
| break; |
| } |
| case 2: |
| memset(&ssid_ie[2], 0, ssid_len_ori); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| return len_diff; |
| } |
| |
| static void issue_beacon(struct adapter *padapter, int timeout_ms) |
| { |
| struct xmit_frame *pmgntframe; |
| struct pkt_attrib *pattrib; |
| unsigned char *pframe; |
| struct ieee80211_hdr *pwlanhdr; |
| __le16 *fctrl; |
| unsigned int rate_len; |
| struct xmit_priv *pxmitpriv = &padapter->xmitpriv; |
| struct mlme_priv *pmlmepriv = &padapter->mlmepriv; |
| struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv; |
| struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info; |
| struct wlan_bssid_ex *cur_network = &pmlmeinfo->network; |
| u8 bc_addr[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; |
| |
| pmgntframe = alloc_mgtxmitframe(pxmitpriv); |
| if (!pmgntframe) { |
| DBG_88E("%s, alloc mgnt frame fail\n", __func__); |
| return; |
| } |
| #if defined(CONFIG_88EU_AP_MODE) |
| spin_lock_bh(&pmlmepriv->bcn_update_lock); |
| #endif |
| |
| /* update attribute */ |
| pattrib = &pmgntframe->attrib; |
| update_mgntframe_attrib(padapter, pattrib); |
| pattrib->qsel = 0x10; |
| |
| memset(pmgntframe->buf_addr, 0, WLANHDR_OFFSET + TXDESC_OFFSET); |
| |
| pframe = (u8 *)(pmgntframe->buf_addr) + TXDESC_OFFSET; |
| pwlanhdr = (struct ieee80211_hdr *)pframe; |
| |
| fctrl = &pwlanhdr->frame_control; |
| *(fctrl) = 0; |
| |
| ether_addr_copy(pwlanhdr->addr1, bc_addr); |
| ether_addr_copy(pwlanhdr->addr2, myid(&padapter->eeprompriv)); |
| ether_addr_copy(pwlanhdr->addr3, cur_network->MacAddress); |
| |
| SetSeqNum(pwlanhdr, 0/*pmlmeext->mgnt_seq*/); |
| /* pmlmeext->mgnt_seq++; */ |
| SetFrameSubType(pframe, WIFI_BEACON); |
| |
| pframe += sizeof(struct ieee80211_hdr_3addr); |
| pattrib->pktlen = sizeof(struct ieee80211_hdr_3addr); |
| |
| if ((pmlmeinfo->state & 0x03) == WIFI_FW_AP_STATE) { |
| int len_diff; |
| u8 *wps_ie; |
| uint wps_ielen; |
| u8 sr = 0; |
| |
| memcpy(pframe, cur_network->ies, cur_network->ie_length); |
| len_diff = update_hidden_ssid( |
| pframe+_BEACON_IE_OFFSET_ |
| , cur_network->ie_length-_BEACON_IE_OFFSET_ |
| , pmlmeinfo->hidden_ssid_mode |
| ); |
| pframe += (cur_network->ie_length+len_diff); |
| pattrib->pktlen += (cur_network->ie_length+len_diff); |
| wps_ie = rtw_get_wps_ie(pmgntframe->buf_addr+TXDESC_OFFSET+sizeof(struct ieee80211_hdr_3addr)+_BEACON_IE_OFFSET_, |
| pattrib->pktlen-sizeof(struct ieee80211_hdr_3addr)-_BEACON_IE_OFFSET_, NULL, &wps_ielen); |
| if (wps_ie && wps_ielen > 0) |
| rtw_get_wps_attr_content(wps_ie, wps_ielen, WPS_ATTR_SELECTED_REGISTRAR, (u8 *)(&sr), NULL); |
| if (sr != 0) |
| set_fwstate(pmlmepriv, WIFI_UNDER_WPS); |
| else |
| _clr_fwstate_(pmlmepriv, WIFI_UNDER_WPS); |
| |
| goto _issue_bcn; |
| } |
| |
| /* below for ad-hoc mode */ |
| |
| /* timestamp will be inserted by hardware */ |
| pframe += 8; |
| pattrib->pktlen += 8; |
| |
| /* beacon interval: 2 bytes */ |
| |
| memcpy(pframe, (unsigned char *)(rtw_get_beacon_interval_from_ie(cur_network->ies)), 2); |
| |
| pframe += 2; |
| pattrib->pktlen += 2; |
| |
| /* capability info: 2 bytes */ |
| |
| memcpy(pframe, (unsigned char *)(rtw_get_capability_from_ie(cur_network->ies)), 2); |
| |
| pframe += 2; |
| pattrib->pktlen += 2; |
| |
| /* SSID */ |
| pframe = rtw_set_ie(pframe, _SSID_IE_, cur_network->ssid.ssid_length, cur_network->ssid.ssid, &pattrib->pktlen); |
| |
| /* supported rates... */ |
| rate_len = rtw_get_rateset_len(cur_network->SupportedRates); |
| pframe = rtw_set_ie(pframe, _SUPPORTEDRATES_IE_, min_t(unsigned int, rate_len, 8), cur_network->SupportedRates, &pattrib->pktlen); |
| |
| /* DS parameter set */ |
| pframe = rtw_set_ie(pframe, _DSSET_IE_, 1, (unsigned char *)&cur_network->Configuration.DSConfig, &pattrib->pktlen); |
| |
| { |
| u8 erpinfo = 0; |
| u32 ATIMWindow; |
| /* IBSS Parameter Set... */ |
| ATIMWindow = 0; |
| pframe = rtw_set_ie(pframe, _IBSS_PARA_IE_, 2, (unsigned char *)(&ATIMWindow), &pattrib->pktlen); |
| |
| /* ERP IE */ |
| pframe = rtw_set_ie(pframe, _ERPINFO_IE_, 1, &erpinfo, &pattrib->pktlen); |
| } |
| |
| /* EXTERNDED SUPPORTED RATE */ |
| if (rate_len > 8) |
| pframe = rtw_set_ie(pframe, _EXT_SUPPORTEDRATES_IE_, (rate_len - 8), (cur_network->SupportedRates + 8), &pattrib->pktlen); |
| /* todo:HT for adhoc */ |
| _issue_bcn: |
| |
| #if defined(CONFIG_88EU_AP_MODE) |
| pmlmepriv->update_bcn = false; |
| |
| spin_unlock_bh(&pmlmepriv->bcn_update_lock); |
| #endif |
| |
| if ((pattrib->pktlen + TXDESC_SIZE) > 512) { |
| DBG_88E("beacon frame too large\n"); |
| return; |
| } |
| |
| pattrib->last_txcmdsz = pattrib->pktlen; |
| |
| /* DBG_88E("issue bcn_sz=%d\n", pattrib->last_txcmdsz); */ |
| if (timeout_ms > 0) |
| dump_mgntframe_and_wait(padapter, pmgntframe, timeout_ms); |
| else |
| dump_mgntframe(padapter, pmgntframe); |
| } |
| |
| static void issue_probersp(struct adapter *padapter, unsigned char *da) |
| { |
| struct xmit_frame *pmgntframe; |
| struct pkt_attrib *pattrib; |
| unsigned char *pframe; |
| struct ieee80211_hdr *pwlanhdr; |
| __le16 *fctrl; |
| unsigned char *mac, *bssid; |
| struct xmit_priv *pxmitpriv = &padapter->xmitpriv; |
| #if defined(CONFIG_88EU_AP_MODE) |
| u8 *pwps_ie; |
| uint wps_ielen; |
| struct mlme_priv *pmlmepriv = &padapter->mlmepriv; |
| #endif |
| struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv; |
| struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info; |
| struct wlan_bssid_ex *cur_network = &pmlmeinfo->network; |
| unsigned int rate_len; |
| |
| pmgntframe = alloc_mgtxmitframe(pxmitpriv); |
| if (!pmgntframe) { |
| DBG_88E("%s, alloc mgnt frame fail\n", __func__); |
| return; |
| } |
| |
| /* update attribute */ |
| pattrib = &pmgntframe->attrib; |
| update_mgntframe_attrib(padapter, pattrib); |
| |
| memset(pmgntframe->buf_addr, 0, WLANHDR_OFFSET + TXDESC_OFFSET); |
| |
| pframe = (u8 *)(pmgntframe->buf_addr) + TXDESC_OFFSET; |
| pwlanhdr = (struct ieee80211_hdr *)pframe; |
| |
| mac = myid(&padapter->eeprompriv); |
| bssid = cur_network->MacAddress; |
| |
| fctrl = &pwlanhdr->frame_control; |
| *(fctrl) = 0; |
| ether_addr_copy(pwlanhdr->addr1, da); |
| ether_addr_copy(pwlanhdr->addr2, mac); |
| ether_addr_copy(pwlanhdr->addr3, bssid); |
| |
| SetSeqNum(pwlanhdr, pmlmeext->mgnt_seq); |
| pmlmeext->mgnt_seq++; |
| SetFrameSubType(fctrl, WIFI_PROBERSP); |
| |
| pattrib->hdrlen = sizeof(struct ieee80211_hdr_3addr); |
| pattrib->pktlen = pattrib->hdrlen; |
| pframe += pattrib->hdrlen; |
| |
| if (cur_network->ie_length > MAX_IE_SZ) |
| return; |
| |
| #if defined(CONFIG_88EU_AP_MODE) |
| if ((pmlmeinfo->state & 0x03) == WIFI_FW_AP_STATE) { |
| pwps_ie = rtw_get_wps_ie(cur_network->ies+_FIXED_IE_LENGTH_, cur_network->ie_length-_FIXED_IE_LENGTH_, NULL, &wps_ielen); |
| |
| /* inerset & update wps_probe_resp_ie */ |
| if ((pmlmepriv->wps_probe_resp_ie != NULL) && pwps_ie && (wps_ielen > 0)) { |
| uint wps_offset, remainder_ielen; |
| u8 *premainder_ie; |
| |
| wps_offset = (uint)(pwps_ie - cur_network->ies); |
| |
| premainder_ie = pwps_ie + wps_ielen; |
| |
| remainder_ielen = cur_network->ie_length - wps_offset - wps_ielen; |
| |
| memcpy(pframe, cur_network->ies, wps_offset); |
| pframe += wps_offset; |
| pattrib->pktlen += wps_offset; |
| |
| wps_ielen = (uint)pmlmepriv->wps_probe_resp_ie[1];/* to get ie data len */ |
| if ((wps_offset+wps_ielen+2) <= MAX_IE_SZ) { |
| memcpy(pframe, pmlmepriv->wps_probe_resp_ie, wps_ielen+2); |
| pframe += wps_ielen+2; |
| pattrib->pktlen += wps_ielen+2; |
| } |
| |
| if ((wps_offset+wps_ielen+2+remainder_ielen) <= MAX_IE_SZ) { |
| memcpy(pframe, premainder_ie, remainder_ielen); |
| pframe += remainder_ielen; |
| pattrib->pktlen += remainder_ielen; |
| } |
| } else { |
| memcpy(pframe, cur_network->ies, cur_network->ie_length); |
| pframe += cur_network->ie_length; |
| pattrib->pktlen += cur_network->ie_length; |
| } |
| } else |
| #endif |
| { |
| /* timestamp will be inserted by hardware */ |
| pframe += 8; |
| pattrib->pktlen += 8; |
| |
| /* beacon interval: 2 bytes */ |
| |
| memcpy(pframe, (unsigned char *)(rtw_get_beacon_interval_from_ie(cur_network->ies)), 2); |
| |
| pframe += 2; |
| pattrib->pktlen += 2; |
| |
| /* capability info: 2 bytes */ |
| |
| memcpy(pframe, (unsigned char *)(rtw_get_capability_from_ie(cur_network->ies)), 2); |
| |
| pframe += 2; |
| pattrib->pktlen += 2; |
| |
| /* below for ad-hoc mode */ |
| |
| /* SSID */ |
| pframe = rtw_set_ie(pframe, _SSID_IE_, cur_network->ssid.ssid_length, cur_network->ssid.ssid, &pattrib->pktlen); |
| |
| /* supported rates... */ |
| rate_len = rtw_get_rateset_len(cur_network->SupportedRates); |
| pframe = rtw_set_ie(pframe, _SUPPORTEDRATES_IE_, min_t(unsigned int, rate_len, 8), cur_network->SupportedRates, &pattrib->pktlen); |
| |
| /* DS parameter set */ |
| pframe = rtw_set_ie(pframe, _DSSET_IE_, 1, (unsigned char *)&cur_network->Configuration.DSConfig, &pattrib->pktlen); |
| |
| if ((pmlmeinfo->state & 0x03) == WIFI_FW_ADHOC_STATE) { |
| u8 erpinfo = 0; |
| u32 ATIMWindow; |
| /* IBSS Parameter Set... */ |
| /* ATIMWindow = cur->Configuration.ATIMWindow; */ |
| ATIMWindow = 0; |
| pframe = rtw_set_ie(pframe, _IBSS_PARA_IE_, 2, (unsigned char *)(&ATIMWindow), &pattrib->pktlen); |
| |
| /* ERP IE */ |
| pframe = rtw_set_ie(pframe, _ERPINFO_IE_, 1, &erpinfo, &pattrib->pktlen); |
| } |
| |
| /* EXTERNDED SUPPORTED RATE */ |
| if (rate_len > 8) |
| pframe = rtw_set_ie(pframe, _EXT_SUPPORTEDRATES_IE_, (rate_len - 8), (cur_network->SupportedRates + 8), &pattrib->pktlen); |
| /* todo:HT for adhoc */ |
| } |
| |
| pattrib->last_txcmdsz = pattrib->pktlen; |
| |
| dump_mgntframe(padapter, pmgntframe); |
| } |
| |
| static int issue_probereq(struct adapter *padapter, |
| struct ndis_802_11_ssid *pssid, u8 *da, |
| bool wait_ack) |
| { |
| int ret = _FAIL; |
| struct xmit_frame *pmgntframe; |
| struct pkt_attrib *pattrib; |
| unsigned char *pframe; |
| struct ieee80211_hdr *pwlanhdr; |
| __le16 *fctrl; |
| unsigned char *mac; |
| unsigned char bssrate[NumRates]; |
| struct xmit_priv *pxmitpriv = &padapter->xmitpriv; |
| struct mlme_priv *pmlmepriv = &padapter->mlmepriv; |
| struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv; |
| int bssrate_len = 0; |
| u8 bc_addr[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; |
| |
| RT_TRACE(_module_rtl871x_mlme_c_, _drv_notice_, ("+%s\n", __func__)); |
| |
| pmgntframe = alloc_mgtxmitframe(pxmitpriv); |
| if (!pmgntframe) |
| goto exit; |
| |
| /* update attribute */ |
| pattrib = &pmgntframe->attrib; |
| update_mgntframe_attrib(padapter, pattrib); |
| |
| memset(pmgntframe->buf_addr, 0, WLANHDR_OFFSET + TXDESC_OFFSET); |
| |
| pframe = (u8 *)(pmgntframe->buf_addr) + TXDESC_OFFSET; |
| pwlanhdr = (struct ieee80211_hdr *)pframe; |
| |
| mac = myid(&padapter->eeprompriv); |
| |
| fctrl = &pwlanhdr->frame_control; |
| *(fctrl) = 0; |
| |
| if (da) { |
| /* unicast probe request frame */ |
| ether_addr_copy(pwlanhdr->addr1, da); |
| ether_addr_copy(pwlanhdr->addr3, da); |
| } else { |
| /* broadcast probe request frame */ |
| ether_addr_copy(pwlanhdr->addr1, bc_addr); |
| ether_addr_copy(pwlanhdr->addr3, bc_addr); |
| } |
| |
| ether_addr_copy(pwlanhdr->addr2, mac); |
| |
| SetSeqNum(pwlanhdr, pmlmeext->mgnt_seq); |
| pmlmeext->mgnt_seq++; |
| SetFrameSubType(pframe, WIFI_PROBEREQ); |
| |
| pframe += sizeof(struct ieee80211_hdr_3addr); |
| pattrib->pktlen = sizeof(struct ieee80211_hdr_3addr); |
| |
| if (pssid) |
| pframe = rtw_set_ie(pframe, _SSID_IE_, pssid->ssid_length, pssid->ssid, &pattrib->pktlen); |
| else |
| pframe = rtw_set_ie(pframe, _SSID_IE_, 0, NULL, &pattrib->pktlen); |
| |
| get_rate_set(padapter, bssrate, &bssrate_len); |
| |
| if (bssrate_len > 8) { |
| pframe = rtw_set_ie(pframe, _SUPPORTEDRATES_IE_, 8, bssrate, &pattrib->pktlen); |
| pframe = rtw_set_ie(pframe, _EXT_SUPPORTEDRATES_IE_, bssrate_len - 8, bssrate + 8, &pattrib->pktlen); |
| } else { |
| pframe = rtw_set_ie(pframe, _SUPPORTEDRATES_IE_, bssrate_len, bssrate, &pattrib->pktlen); |
| } |
| |
| /* add wps_ie for wps2.0 */ |
| if (pmlmepriv->wps_probe_req_ie_len > 0 && pmlmepriv->wps_probe_req_ie) { |
| memcpy(pframe, pmlmepriv->wps_probe_req_ie, pmlmepriv->wps_probe_req_ie_len); |
| pframe += pmlmepriv->wps_probe_req_ie_len; |
| pattrib->pktlen += pmlmepriv->wps_probe_req_ie_len; |
| } |
| |
| pattrib->last_txcmdsz = pattrib->pktlen; |
| |
| RT_TRACE(_module_rtl871x_mlme_c_, _drv_notice_, |
| ("issuing probe_req, tx_len=%d\n", pattrib->last_txcmdsz)); |
| |
| if (wait_ack) { |
| ret = dump_mgntframe_and_wait_ack(padapter, pmgntframe); |
| } else { |
| dump_mgntframe(padapter, pmgntframe); |
| ret = _SUCCESS; |
| } |
| |
| exit: |
| return ret; |
| } |
| |
| static int issue_probereq_ex(struct adapter *padapter, |
| struct ndis_802_11_ssid *pssid, u8 *da, |
| int try_cnt, int wait_ms) |
| { |
| int ret; |
| int i = 0; |
| unsigned long start = jiffies; |
| |
| do { |
| ret = issue_probereq(padapter, pssid, da, wait_ms > 0); |
| |
| i++; |
| |
| if (padapter->bDriverStopped || padapter->bSurpriseRemoved) |
| break; |
| |
| if (i < try_cnt && wait_ms > 0 && ret == _FAIL) |
| msleep(wait_ms); |
| |
| } while ((i < try_cnt) && ((ret == _FAIL) || (wait_ms == 0))); |
| |
| if (ret != _FAIL) { |
| ret = _SUCCESS; |
| goto exit; |
| } |
| |
| if (try_cnt && wait_ms) { |
| if (da) |
| DBG_88E(FUNC_ADPT_FMT" to %pM, ch:%u%s, %d/%d in %u ms\n", |
| FUNC_ADPT_ARG(padapter), da, rtw_get_oper_ch(padapter), |
| ret == _SUCCESS ? ", acked" : "", i, try_cnt, |
| jiffies_to_msecs(jiffies - start)); |
| else |
| DBG_88E(FUNC_ADPT_FMT", ch:%u%s, %d/%d in %u ms\n", |
| FUNC_ADPT_ARG(padapter), rtw_get_oper_ch(padapter), |
| ret == _SUCCESS ? ", acked" : "", i, try_cnt, |
| jiffies_to_msecs(jiffies - start)); |
| } |
| exit: |
| return ret; |
| } |
| |
| /* if psta == NULL, indicate we are station(client) now... */ |
| static void issue_auth(struct adapter *padapter, struct sta_info *psta, |
| unsigned short status) |
| { |
| struct xmit_frame *pmgntframe; |
| struct pkt_attrib *pattrib; |
| unsigned char *pframe; |
| struct ieee80211_hdr *pwlanhdr; |
| __le16 *fctrl; |
| unsigned int val32; |
| u16 val16; |
| #ifdef CONFIG_88EU_AP_MODE |
| __le16 le_val16; |
| #endif |
| int use_shared_key = 0; |
| struct xmit_priv *pxmitpriv = &padapter->xmitpriv; |
| struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv; |
| struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info; |
| struct wlan_bssid_ex *pnetwork = &pmlmeinfo->network; |
| |
| pmgntframe = alloc_mgtxmitframe(pxmitpriv); |
| if (pmgntframe == NULL) |
| return; |
| |
| /* update attribute */ |
| pattrib = &pmgntframe->attrib; |
| update_mgntframe_attrib(padapter, pattrib); |
| |
| memset(pmgntframe->buf_addr, 0, WLANHDR_OFFSET + TXDESC_OFFSET); |
| |
| pframe = (u8 *)(pmgntframe->buf_addr) + TXDESC_OFFSET; |
| pwlanhdr = (struct ieee80211_hdr *)pframe; |
| |
| fctrl = &pwlanhdr->frame_control; |
| *(fctrl) = 0; |
| |
| SetSeqNum(pwlanhdr, pmlmeext->mgnt_seq); |
| pmlmeext->mgnt_seq++; |
| SetFrameSubType(pframe, WIFI_AUTH); |
| |
| pframe += sizeof(struct ieee80211_hdr_3addr); |
| pattrib->pktlen = sizeof(struct ieee80211_hdr_3addr); |
| |
| if (psta) {/* for AP mode */ |
| #ifdef CONFIG_88EU_AP_MODE |
| |
| ether_addr_copy(pwlanhdr->addr1, psta->hwaddr); |
| ether_addr_copy(pwlanhdr->addr2, |
| myid(&padapter->eeprompriv)); |
| ether_addr_copy(pwlanhdr->addr3, |
| myid(&padapter->eeprompriv)); |
| |
| /* setting auth algo number */ |
| val16 = (u16)psta->authalg; |
| |
| if (status != _STATS_SUCCESSFUL_) |
| val16 = 0; |
| |
| if (val16) { |
| le_val16 = cpu_to_le16(val16); |
| use_shared_key = 1; |
| } else { |
| le_val16 = 0; |
| } |
| |
| pframe = rtw_set_fixed_ie(pframe, _AUTH_ALGM_NUM_, &le_val16, |
| &pattrib->pktlen); |
| |
| /* setting auth seq number */ |
| val16 = (u16)psta->auth_seq; |
| le_val16 = cpu_to_le16(val16); |
| pframe = rtw_set_fixed_ie(pframe, _AUTH_SEQ_NUM_, &le_val16, |
| &pattrib->pktlen); |
| |
| /* setting status code... */ |
| val16 = status; |
| le_val16 = cpu_to_le16(val16); |
| pframe = rtw_set_fixed_ie(pframe, _STATUS_CODE_, |
| &le_val16, &pattrib->pktlen); |
| |
| /* added challenging text... */ |
| if ((psta->auth_seq == 2) && (psta->state & WIFI_FW_AUTH_STATE) && (use_shared_key == 1)) |
| pframe = rtw_set_ie(pframe, _CHLGETXT_IE_, 128, psta->chg_txt, &pattrib->pktlen); |
| #endif |
| } else { |
| __le32 le_tmp32; |
| __le16 le_tmp16; |
| |
| ether_addr_copy(pwlanhdr->addr1, pnetwork->MacAddress); |
| ether_addr_copy(pwlanhdr->addr2, myid(&padapter->eeprompriv)); |
| ether_addr_copy(pwlanhdr->addr3, pnetwork->MacAddress); |
| |
| /* setting auth algo number */ |
| val16 = (pmlmeinfo->auth_algo == dot11AuthAlgrthm_Shared) ? 1 : 0;/* 0:OPEN System, 1:Shared key */ |
| if (val16) |
| use_shared_key = 1; |
| |
| /* setting IV for auth seq #3 */ |
| if ((pmlmeinfo->auth_seq == 3) && (pmlmeinfo->state & WIFI_FW_AUTH_STATE) && (use_shared_key == 1)) { |
| val32 = (pmlmeinfo->iv++) | (pmlmeinfo->key_index << 30); |
| le_tmp32 = cpu_to_le32(val32); |
| pframe = rtw_set_fixed_ie(pframe, 4, &le_tmp32, |
| &pattrib->pktlen); |
| |
| pattrib->iv_len = 4; |
| } |
| |
| le_tmp16 = cpu_to_le16(val16); |
| pframe = rtw_set_fixed_ie(pframe, _AUTH_ALGM_NUM_, &le_tmp16, |
| &pattrib->pktlen); |
| |
| /* setting auth seq number */ |
| val16 = pmlmeinfo->auth_seq; |
| le_tmp16 = cpu_to_le16(val16); |
| pframe = rtw_set_fixed_ie(pframe, _AUTH_SEQ_NUM_, &le_tmp16, |
| &pattrib->pktlen); |
| |
| /* setting status code... */ |
| le_tmp16 = cpu_to_le16(status); |
| pframe = rtw_set_fixed_ie(pframe, _STATUS_CODE_, &le_tmp16, |
| &pattrib->pktlen); |
| |
| /* then checking to see if sending challenging text... */ |
| if ((pmlmeinfo->auth_seq == 3) && (pmlmeinfo->state & WIFI_FW_AUTH_STATE) && (use_shared_key == 1)) { |
| pframe = rtw_set_ie(pframe, _CHLGETXT_IE_, 128, pmlmeinfo->chg_txt, &pattrib->pktlen); |
| |
| SetPrivacy(fctrl); |
| |
| pattrib->hdrlen = sizeof(struct ieee80211_hdr_3addr); |
| |
| pattrib->encrypt = _WEP40_; |
| |
| pattrib->icv_len = 4; |
| |
| pattrib->pktlen += pattrib->icv_len; |
| } |
| } |
| |
| pattrib->last_txcmdsz = pattrib->pktlen; |
| |
| rtw_wep_encrypt(padapter, (u8 *)pmgntframe); |
| DBG_88E("%s\n", __func__); |
| dump_mgntframe(padapter, pmgntframe); |
| } |
| |
| #ifdef CONFIG_88EU_AP_MODE |
| static void issue_asocrsp(struct adapter *padapter, unsigned short status, |
| struct sta_info *pstat, int pkt_type) |
| { |
| struct xmit_frame *pmgntframe; |
| struct ieee80211_hdr *pwlanhdr; |
| struct pkt_attrib *pattrib; |
| unsigned char *pbuf, *pframe; |
| unsigned short val; |
| __le16 *fctrl; |
| struct xmit_priv *pxmitpriv = &padapter->xmitpriv; |
| struct mlme_priv *pmlmepriv = &padapter->mlmepriv; |
| struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv; |
| struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info; |
| struct wlan_bssid_ex *pnetwork = &pmlmeinfo->network; |
| u8 *ie = pnetwork->ies; |
| __le16 lestatus, leval; |
| |
| DBG_88E("%s\n", __func__); |
| |
| pmgntframe = alloc_mgtxmitframe(pxmitpriv); |
| if (!pmgntframe) |
| return; |
| |
| /* update attribute */ |
| pattrib = &pmgntframe->attrib; |
| update_mgntframe_attrib(padapter, pattrib); |
| |
| memset(pmgntframe->buf_addr, 0, WLANHDR_OFFSET + TXDESC_OFFSET); |
| |
| pframe = (u8 *)(pmgntframe->buf_addr) + TXDESC_OFFSET; |
| pwlanhdr = (struct ieee80211_hdr *)pframe; |
| |
| fctrl = &pwlanhdr->frame_control; |
| *(fctrl) = 0; |
| |
| ether_addr_copy((void *)GetAddr1Ptr(pwlanhdr), pstat->hwaddr); |
| ether_addr_copy((void *)GetAddr2Ptr(pwlanhdr), |
| myid(&padapter->eeprompriv)); |
| ether_addr_copy((void *)GetAddr3Ptr(pwlanhdr), pnetwork->MacAddress); |
| |
| SetSeqNum(pwlanhdr, pmlmeext->mgnt_seq); |
| pmlmeext->mgnt_seq++; |
| if ((pkt_type == WIFI_ASSOCRSP) || (pkt_type == WIFI_REASSOCRSP)) |
| SetFrameSubType(pwlanhdr, pkt_type); |
| else |
| return; |
| |
| pattrib->hdrlen = sizeof(struct ieee80211_hdr_3addr); |
| pattrib->pktlen += pattrib->hdrlen; |
| pframe += pattrib->hdrlen; |
| |
| /* capability */ |
| val = *(unsigned short *)rtw_get_capability_from_ie(ie); |
| |
| pframe = rtw_set_fixed_ie(pframe, _CAPABILITY_, &val, &pattrib->pktlen); |
| |
| lestatus = cpu_to_le16(status); |
| pframe = rtw_set_fixed_ie(pframe, _STATUS_CODE_, &lestatus, |
| &pattrib->pktlen); |
| |
| leval = cpu_to_le16(pstat->aid | BIT(14) | BIT(15)); |
| pframe = rtw_set_fixed_ie(pframe, _ASOC_ID_, &leval, &pattrib->pktlen); |
| |
| if (pstat->bssratelen <= 8) { |
| pframe = rtw_set_ie(pframe, _SUPPORTEDRATES_IE_, pstat->bssratelen, pstat->bssrateset, &pattrib->pktlen); |
| } else { |
| pframe = rtw_set_ie(pframe, _SUPPORTEDRATES_IE_, 8, pstat->bssrateset, &pattrib->pktlen); |
| pframe = rtw_set_ie(pframe, _EXT_SUPPORTEDRATES_IE_, pstat->bssratelen-8, pstat->bssrateset+8, &pattrib->pktlen); |
| } |
| |
| if ((pstat->flags & WLAN_STA_HT) && (pmlmepriv->htpriv.ht_option)) { |
| uint ie_len = 0; |
| |
| /* FILL HT CAP INFO IE */ |
| pbuf = rtw_get_ie(ie + _BEACON_IE_OFFSET_, _HT_CAPABILITY_IE_, &ie_len, (pnetwork->ie_length - _BEACON_IE_OFFSET_)); |
| if (pbuf && ie_len > 0) { |
| memcpy(pframe, pbuf, ie_len+2); |
| pframe += (ie_len+2); |
| pattrib->pktlen += (ie_len+2); |
| } |
| |
| /* FILL HT ADD INFO IE */ |
| pbuf = rtw_get_ie(ie + _BEACON_IE_OFFSET_, _HT_ADD_INFO_IE_, &ie_len, (pnetwork->ie_length - _BEACON_IE_OFFSET_)); |
| if (pbuf && ie_len > 0) { |
| memcpy(pframe, pbuf, ie_len+2); |
| pframe += (ie_len+2); |
| pattrib->pktlen += (ie_len+2); |
| } |
| } |
| |
| /* FILL WMM IE */ |
| if ((pstat->flags & WLAN_STA_WME) && (pmlmepriv->qospriv.qos_option)) { |
| uint ie_len = 0; |
| unsigned char WMM_PARA_IE[] = {0x00, 0x50, 0xf2, 0x02, 0x01, 0x01}; |
| |
| for (pbuf = ie + _BEACON_IE_OFFSET_;; pbuf += (ie_len + 2)) { |
| pbuf = rtw_get_ie(pbuf, _VENDOR_SPECIFIC_IE_, &ie_len, (pnetwork->ie_length - _BEACON_IE_OFFSET_ - (ie_len + 2))); |
| if (pbuf && !memcmp(pbuf+2, WMM_PARA_IE, 6)) { |
| memcpy(pframe, pbuf, ie_len+2); |
| pframe += (ie_len+2); |
| pattrib->pktlen += (ie_len+2); |
| break; |
| } |
| |
| if ((pbuf == NULL) || (ie_len == 0)) |
| break; |
| } |
| } |
| |
| if (pmlmeinfo->assoc_AP_vendor == HT_IOT_PEER_REALTEK) |
| pframe = rtw_set_ie(pframe, _VENDOR_SPECIFIC_IE_, 6, REALTEK_96B_IE, &pattrib->pktlen); |
| |
| /* add WPS IE ie for wps 2.0 */ |
| if (pmlmepriv->wps_assoc_resp_ie && pmlmepriv->wps_assoc_resp_ie_len > 0) { |
| memcpy(pframe, pmlmepriv->wps_assoc_resp_ie, pmlmepriv->wps_assoc_resp_ie_len); |
| |
| pframe += pmlmepriv->wps_assoc_resp_ie_len; |
| pattrib->pktlen += pmlmepriv->wps_assoc_resp_ie_len; |
| } |
| |
| pattrib->last_txcmdsz = pattrib->pktlen; |
| dump_mgntframe(padapter, pmgntframe); |
| } |
| #endif /* CONFIG_88EU_AP_MODE */ |
| |
| static void issue_assocreq(struct adapter *padapter) |
| { |
| int ret = _FAIL; |
| struct xmit_frame *pmgntframe; |
| struct pkt_attrib *pattrib; |
| unsigned char *pframe, *p; |
| struct ieee80211_hdr *pwlanhdr; |
| __le16 *fctrl; |
| unsigned int i, j, ie_len, index = 0; |
| unsigned char bssrate[NumRates], sta_bssrate[NumRates]; |
| struct ndis_802_11_var_ie *pIE; |
| struct registry_priv *pregpriv = &padapter->registrypriv; |
| struct xmit_priv *pxmitpriv = &padapter->xmitpriv; |
| struct mlme_priv *pmlmepriv = &padapter->mlmepriv; |
| struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv; |
| struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info; |
| int bssrate_len = 0, sta_bssrate_len = 0; |
| struct wlan_bssid_ex *pnetwork = &pmlmeinfo->network; |
| |
| pmgntframe = alloc_mgtxmitframe(pxmitpriv); |
| if (!pmgntframe) |
| goto exit; |
| |
| /* update attribute */ |
| pattrib = &pmgntframe->attrib; |
| update_mgntframe_attrib(padapter, pattrib); |
| |
| memset(pmgntframe->buf_addr, 0, WLANHDR_OFFSET + TXDESC_OFFSET); |
| pframe = (u8 *)(pmgntframe->buf_addr) + TXDESC_OFFSET; |
| pwlanhdr = (struct ieee80211_hdr *)pframe; |
| |
| fctrl = &pwlanhdr->frame_control; |
| *(fctrl) = 0; |
| ether_addr_copy(pwlanhdr->addr1, pnetwork->MacAddress); |
| ether_addr_copy(pwlanhdr->addr2, myid(&padapter->eeprompriv)); |
| ether_addr_copy(pwlanhdr->addr3, pnetwork->MacAddress); |
| |
| SetSeqNum(pwlanhdr, pmlmeext->mgnt_seq); |
| pmlmeext->mgnt_seq++; |
| SetFrameSubType(pframe, WIFI_ASSOCREQ); |
| |
| pframe += sizeof(struct ieee80211_hdr_3addr); |
| pattrib->pktlen = sizeof(struct ieee80211_hdr_3addr); |
| |
| /* caps */ |
| |
| memcpy(pframe, rtw_get_capability_from_ie(pmlmeinfo->network.ies), 2); |
| |
| pframe += 2; |
| pattrib->pktlen += 2; |
| |
| /* listen interval */ |
| /* todo: listen interval for power saving */ |
| put_unaligned_le16(3, pframe); |
| pframe += 2; |
| pattrib->pktlen += 2; |
| |
| /* SSID */ |
| pframe = rtw_set_ie(pframe, _SSID_IE_, pmlmeinfo->network.ssid.ssid_length, pmlmeinfo->network.ssid.ssid, &pattrib->pktlen); |
| |
| /* supported rate & extended supported rate */ |
| |
| /* Check if the AP's supported rates are also supported by STA. */ |
| get_rate_set(padapter, sta_bssrate, &sta_bssrate_len); |
| |
| if (pmlmeext->cur_channel == 14)/* for JAPAN, channel 14 can only uses B Mode(CCK) */ |
| sta_bssrate_len = 4; |
| |
| for (i = 0; i < NDIS_802_11_LENGTH_RATES_EX; i++) { |
| if (pmlmeinfo->network.SupportedRates[i] == 0) |
| break; |
| DBG_88E("network.SupportedRates[%d]=%02X\n", i, pmlmeinfo->network.SupportedRates[i]); |
| } |
| |
| for (i = 0; i < NDIS_802_11_LENGTH_RATES_EX; i++) { |
| if (pmlmeinfo->network.SupportedRates[i] == 0) |
| break; |
| |
| /* Check if the AP's supported rates are also supported by STA. */ |
| for (j = 0; j < sta_bssrate_len; j++) { |
| /* Avoid the proprietary data rate (22Mbps) of Handlink WSG-4000 AP */ |
| if ((pmlmeinfo->network.SupportedRates[i]|IEEE80211_BASIC_RATE_MASK) |
| == (sta_bssrate[j]|IEEE80211_BASIC_RATE_MASK)) |
| break; |
| } |
| |
| if (j == sta_bssrate_len) { |
| /* the rate is not supported by STA */ |
| DBG_88E("%s(): the rate[%d]=%02X is not supported by STA!\n", __func__, i, pmlmeinfo->network.SupportedRates[i]); |
| } else { |
| /* the rate is supported by STA */ |
| bssrate[index++] = pmlmeinfo->network.SupportedRates[i]; |
| } |
| } |
| |
| bssrate_len = index; |
| DBG_88E("bssrate_len=%d\n", bssrate_len); |
| |
| if (bssrate_len == 0) { |
| rtw_free_xmitbuf(pxmitpriv, pmgntframe->pxmitbuf); |
| rtw_free_xmitframe(pxmitpriv, pmgntframe); |
| goto exit; /* don't connect to AP if no joint supported rate */ |
| } |
| |
| if (bssrate_len > 8) { |
| pframe = rtw_set_ie(pframe, _SUPPORTEDRATES_IE_, 8, bssrate, &pattrib->pktlen); |
| pframe = rtw_set_ie(pframe, _EXT_SUPPORTEDRATES_IE_, bssrate_len - 8, bssrate + 8, &pattrib->pktlen); |
| } else { |
| pframe = rtw_set_ie(pframe, _SUPPORTEDRATES_IE_, bssrate_len, bssrate, &pattrib->pktlen); |
| } |
| |
| /* RSN */ |
| p = rtw_get_ie((pmlmeinfo->network.ies + sizeof(struct ndis_802_11_fixed_ie)), _RSN_IE_2_, &ie_len, (pmlmeinfo->network.ie_length - sizeof(struct ndis_802_11_fixed_ie))); |
| if (p) |
| pframe = rtw_set_ie(pframe, _RSN_IE_2_, ie_len, p + 2, &pattrib->pktlen); |
| |
| /* HT caps */ |
| if (padapter->mlmepriv.htpriv.ht_option) { |
| p = rtw_get_ie((pmlmeinfo->network.ies + sizeof(struct ndis_802_11_fixed_ie)), _HT_CAPABILITY_IE_, &ie_len, (pmlmeinfo->network.ie_length - sizeof(struct ndis_802_11_fixed_ie))); |
| if ((p != NULL) && (!(is_ap_in_tkip(padapter)))) { |
| memcpy(&pmlmeinfo->HT_caps, p + 2, sizeof(struct ieee80211_ht_cap)); |
| |
| /* to disable 40M Hz support while gd_bw_40MHz_en = 0 */ |
| if (pregpriv->cbw40_enable == 0) |
| pmlmeinfo->HT_caps.cap_info &= cpu_to_le16(~(BIT(6) | BIT(1))); |
| else |
| pmlmeinfo->HT_caps.cap_info |= cpu_to_le16(BIT(1)); |
| |
| /* todo: disable SM power save mode */ |
| pmlmeinfo->HT_caps.cap_info |= cpu_to_le16(0x000c); |
| |
| if (pregpriv->rx_stbc) |
| pmlmeinfo->HT_caps.cap_info |= cpu_to_le16(0x0100);/* RX STBC One spatial stream */ |
| memcpy((u8 *)&pmlmeinfo->HT_caps.mcs, MCS_rate_1R, 16); |
| pframe = rtw_set_ie(pframe, _HT_CAPABILITY_IE_, ie_len, (u8 *)(&pmlmeinfo->HT_caps), &pattrib->pktlen); |
| } |
| } |
| |
| /* vendor specific IE, such as WPA, WMM, WPS */ |
| for (i = sizeof(struct ndis_802_11_fixed_ie); i < pmlmeinfo->network.ie_length; i += (pIE->Length + 2)) { |
| pIE = (struct ndis_802_11_var_ie *)(pmlmeinfo->network.ies + i); |
| |
| switch (pIE->ElementID) { |
| case _VENDOR_SPECIFIC_IE_: |
| if ((!memcmp(pIE->data, RTW_WPA_OUI, 4)) || |
| (!memcmp(pIE->data, WMM_OUI, 4)) || |
| (!memcmp(pIE->data, WPS_OUI, 4))) { |
| if (!padapter->registrypriv.wifi_spec) { |
| /* Commented by Kurt 20110629 */ |
| /* In some older APs, WPS handshake */ |
| /* would be fail if we append vender extensions informations to AP */ |
| if (!memcmp(pIE->data, WPS_OUI, 4)) |
| pIE->Length = 14; |
| } |
| pframe = rtw_set_ie(pframe, _VENDOR_SPECIFIC_IE_, pIE->Length, pIE->data, &pattrib->pktlen); |
| } |
| break; |
| default: |
| break; |
| } |
| } |
| |
| if (pmlmeinfo->assoc_AP_vendor == HT_IOT_PEER_REALTEK) |
| pframe = rtw_set_ie(pframe, _VENDOR_SPECIFIC_IE_, 6, REALTEK_96B_IE, &pattrib->pktlen); |
| |
| pattrib->last_txcmdsz = pattrib->pktlen; |
| dump_mgntframe(padapter, pmgntframe); |
| |
| ret = _SUCCESS; |
| |
| exit: |
| if (ret == _SUCCESS) |
| rtw_buf_update(&pmlmepriv->assoc_req, &pmlmepriv->assoc_req_len, (u8 *)pwlanhdr, pattrib->pktlen); |
| else |
| rtw_buf_free(&pmlmepriv->assoc_req, &pmlmepriv->assoc_req_len); |
| } |
| |
| /* when wait_ack is true, this function should be called at process context */ |
| static int _issue_nulldata(struct adapter *padapter, unsigned char *da, |
| unsigned int power_mode, bool wait_ack) |
| { |
| int ret = _FAIL; |
| struct xmit_frame *pmgntframe; |
| struct pkt_attrib *pattrib; |
| unsigned char *pframe; |
| struct ieee80211_hdr *pwlanhdr; |
| __le16 *fctrl; |
| struct xmit_priv *pxmitpriv; |
| struct mlme_ext_priv *pmlmeext; |
| struct mlme_ext_info *pmlmeinfo; |
| struct wlan_bssid_ex *pnetwork; |
| |
| if (!padapter) |
| goto exit; |
| |
| pxmitpriv = &padapter->xmitpriv; |
| pmlmeext = &padapter->mlmeextpriv; |
| pmlmeinfo = &pmlmeext->mlmext_info; |
| pnetwork = &pmlmeinfo->network; |
| |
| pmgntframe = alloc_mgtxmitframe(pxmitpriv); |
| if (!pmgntframe) |
| goto exit; |
| |
| /* update attribute */ |
| pattrib = &pmgntframe->attrib; |
| update_mgntframe_attrib(padapter, pattrib); |
| pattrib->retry_ctrl = false; |
| |
| memset(pmgntframe->buf_addr, 0, WLANHDR_OFFSET + TXDESC_OFFSET); |
| |
| pframe = (u8 *)(pmgntframe->buf_addr) + TXDESC_OFFSET; |
| pwlanhdr = (struct ieee80211_hdr *)pframe; |
| |
| fctrl = &pwlanhdr->frame_control; |
| *(fctrl) = 0; |
| |
| if ((pmlmeinfo->state & 0x03) == WIFI_FW_AP_STATE) |
| SetFrDs(fctrl); |
| else if ((pmlmeinfo->state & 0x03) == WIFI_FW_STATION_STATE) |
| SetToDs(fctrl); |
| |
| if (power_mode) |
| SetPwrMgt(fctrl); |
| |
| ether_addr_copy(pwlanhdr->addr1, da); |
| ether_addr_copy(pwlanhdr->addr2, myid(&padapter->eeprompriv)); |
| ether_addr_copy(pwlanhdr->addr3, pnetwork->MacAddress); |
| |
| SetSeqNum(pwlanhdr, pmlmeext->mgnt_seq); |
| pmlmeext->mgnt_seq++; |
| SetFrameSubType(pframe, WIFI_DATA_NULL); |
| |
| pframe += sizeof(struct ieee80211_hdr_3addr); |
| pattrib->pktlen = sizeof(struct ieee80211_hdr_3addr); |
| |
| pattrib->last_txcmdsz = pattrib->pktlen; |
| |
| if (wait_ack) { |
| ret = dump_mgntframe_and_wait_ack(padapter, pmgntframe); |
| } else { |
| dump_mgntframe(padapter, pmgntframe); |
| ret = _SUCCESS; |
| } |
| |
| exit: |
| return ret; |
| } |
| |
| /* when wait_ms > 0 , this function should be called at process context */ |
| /* da == NULL for station mode */ |
| int issue_nulldata(struct adapter *padapter, unsigned char *da, |
| unsigned int power_mode, int try_cnt, int wait_ms) |
| { |
| int ret; |
| int i = 0; |
| unsigned long start = jiffies; |
| struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv; |
| struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info; |
| struct wlan_bssid_ex *pnetwork = &pmlmeinfo->network; |
| |
| /* da == NULL, assume it's null data for sta to ap*/ |
| if (da == NULL) |
| da = pnetwork->MacAddress; |
| |
| do { |
| ret = _issue_nulldata(padapter, da, power_mode, wait_ms > 0); |
| |
| i++; |
| |
| if (padapter->bDriverStopped || padapter->bSurpriseRemoved) |
| break; |
| |
| if (i < try_cnt && wait_ms > 0 && ret == _FAIL) |
| msleep(wait_ms); |
| } while ((i < try_cnt) && ((ret == _FAIL) || (wait_ms == 0))); |
| |
| if (ret != _FAIL) { |
| ret = _SUCCESS; |
| goto exit; |
| } |
| |
| if (try_cnt && wait_ms) { |
| if (da) |
| DBG_88E(FUNC_ADPT_FMT" to %pM, ch:%u%s, %d/%d in %u ms\n", |
| FUNC_ADPT_ARG(padapter), da, rtw_get_oper_ch(padapter), |
| ret == _SUCCESS ? ", acked" : "", i, try_cnt, |
| jiffies_to_msecs(jiffies - start)); |
| else |
| DBG_88E(FUNC_ADPT_FMT", ch:%u%s, %d/%d in %u ms\n", |
| FUNC_ADPT_ARG(padapter), rtw_get_oper_ch(padapter), |
| ret == _SUCCESS ? ", acked" : "", i, try_cnt, |
| jiffies_to_msecs(jiffies - start)); |
| } |
| exit: |
| return ret; |
| } |
| |
| /* when wait_ack is true, this function should be called at process context */ |
| static int _issue_qos_nulldata(struct adapter *padapter, unsigned char *da, |
| u16 tid, bool wait_ack) |
| { |
| int ret = _FAIL; |
| struct xmit_frame *pmgntframe; |
| struct pkt_attrib *pattrib; |
| unsigned char *pframe; |
| struct ieee80211_hdr *pwlanhdr; |
| __le16 *fctrl; |
| unsigned short *qc; |
| struct xmit_priv *pxmitpriv = &padapter->xmitpriv; |
| struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv; |
| struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info; |
| struct wlan_bssid_ex *pnetwork = &pmlmeinfo->network; |
| |
| DBG_88E("%s\n", __func__); |
| |
| pmgntframe = alloc_mgtxmitframe(pxmitpriv); |
| if (!pmgntframe) |
| goto exit; |
| |
| /* update attribute */ |
| pattrib = &pmgntframe->attrib; |
| update_mgntframe_attrib(padapter, pattrib); |
| |
| pattrib->hdrlen += 2; |
| pattrib->qos_en = true; |
| pattrib->eosp = 1; |
| pattrib->ack_policy = 0; |
| pattrib->mdata = 0; |
| |
| memset(pmgntframe->buf_addr, 0, WLANHDR_OFFSET + TXDESC_OFFSET); |
| |
| pframe = (u8 *)(pmgntframe->buf_addr) + TXDESC_OFFSET; |
| pwlanhdr = (struct ieee80211_hdr *)pframe; |
| |
| fctrl = &pwlanhdr->frame_control; |
| *(fctrl) = 0; |
| |
| if ((pmlmeinfo->state & 0x03) == WIFI_FW_AP_STATE) |
| SetFrDs(fctrl); |
| else if ((pmlmeinfo->state & 0x03) == WIFI_FW_STATION_STATE) |
| SetToDs(fctrl); |
| |
| if (pattrib->mdata) |
| SetMData(fctrl); |
| |
| qc = (unsigned short *)(pframe + pattrib->hdrlen - 2); |
| |
| SetPriority(qc, tid); |
| |
| SetEOSP(qc, pattrib->eosp); |
| |
| SetAckpolicy(qc, pattrib->ack_policy); |
| |
| ether_addr_copy(pwlanhdr->addr1, da); |
| ether_addr_copy(pwlanhdr->addr2, myid(&padapter->eeprompriv)); |
| ether_addr_copy(pwlanhdr->addr3, pnetwork->MacAddress); |
| |
| SetSeqNum(pwlanhdr, pmlmeext->mgnt_seq); |
| pmlmeext->mgnt_seq++; |
| SetFrameSubType(pframe, WIFI_QOS_DATA_NULL); |
| |
| pframe += sizeof(struct ieee80211_qos_hdr); |
| pattrib->pktlen = sizeof(struct ieee80211_qos_hdr); |
| |
| pattrib->last_txcmdsz = pattrib->pktlen; |
| |
| if (wait_ack) { |
| ret = dump_mgntframe_and_wait_ack(padapter, pmgntframe); |
| } else { |
| dump_mgntframe(padapter, pmgntframe); |
| ret = _SUCCESS; |
| } |
| |
| exit: |
| return ret; |
| } |
| |
| /* when wait_ms > 0 , this function should be called at process context */ |
| /* da == NULL for station mode */ |
| int issue_qos_nulldata(struct adapter *padapter, unsigned char *da, |
| u16 tid, int try_cnt, int wait_ms) |
| { |
| int ret; |
| int i = 0; |
| unsigned long start = jiffies; |
| struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv; |
| struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info; |
| struct wlan_bssid_ex *pnetwork = &pmlmeinfo->network; |
| |
| /* da == NULL, assume it's null data for sta to ap*/ |
| if (da == NULL) |
| da = pnetwork->MacAddress; |
| |
| do { |
| ret = _issue_qos_nulldata(padapter, da, tid, wait_ms > 0); |
| |
| i++; |
| |
| if (padapter->bDriverStopped || padapter->bSurpriseRemoved) |
| break; |
| |
| if (i < try_cnt && wait_ms > 0 && ret == _FAIL) |
| msleep(wait_ms); |
| } while ((i < try_cnt) && ((ret == _FAIL) || (wait_ms == 0))); |
| |
| if (ret != _FAIL) { |
| ret = _SUCCESS; |
| goto exit; |
| } |
| |
| if (try_cnt && wait_ms) { |
| if (da) |
| DBG_88E(FUNC_ADPT_FMT" to %pM, ch:%u%s, %d/%d in %u ms\n", |
| FUNC_ADPT_ARG(padapter), da, rtw_get_oper_ch(padapter), |
| ret == _SUCCESS ? ", acked" : "", i, try_cnt, |
| jiffies_to_msecs(jiffies - start)); |
| else |
| DBG_88E(FUNC_ADPT_FMT", ch:%u%s, %d/%d in %u ms\n", |
| FUNC_ADPT_ARG(padapter), rtw_get_oper_ch(padapter), |
| ret == _SUCCESS ? ", acked" : "", i, try_cnt, |
| jiffies_to_msecs(jiffies - start)); |
| } |
| exit: |
| return ret; |
| } |
| |
| static int _issue_deauth(struct adapter *padapter, unsigned char *da, |
| unsigned short reason, bool wait_ack) |
| { |
| struct xmit_frame *pmgntframe; |
| struct pkt_attrib *pattrib; |
| unsigned char *pframe; |
| struct ieee80211_hdr *pwlanhdr; |
| __le16 *fctrl; |
| struct xmit_priv *pxmitpriv = &padapter->xmitpriv; |
| struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv; |
| struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info; |
| struct wlan_bssid_ex *pnetwork = &pmlmeinfo->network; |
| int ret = _FAIL; |
| __le16 le_tmp; |
| |
| pmgntframe = alloc_mgtxmitframe(pxmitpriv); |
| if (pmgntframe == NULL) |
| goto exit; |
| |
| /* update attribute */ |
| pattrib = &pmgntframe->attrib; |
| update_mgntframe_attrib(padapter, pattrib); |
| pattrib->retry_ctrl = false; |
| |
| memset(pmgntframe->buf_addr, 0, WLANHDR_OFFSET + TXDESC_OFFSET); |
| |
| pframe = (u8 *)(pmgntframe->buf_addr) + TXDESC_OFFSET; |
| pwlanhdr = (struct ieee80211_hdr *)pframe; |
| |
| fctrl = &pwlanhdr->frame_control; |
| *(fctrl) = 0; |
| |
| ether_addr_copy(pwlanhdr->addr1, da); |
| ether_addr_copy(pwlanhdr->addr2, myid(&padapter->eeprompriv)); |
| ether_addr_copy(pwlanhdr->addr3, pnetwork->MacAddress); |
| |
| SetSeqNum(pwlanhdr, pmlmeext->mgnt_seq); |
| pmlmeext->mgnt_seq++; |
| SetFrameSubType(pframe, WIFI_DEAUTH); |
| |
| pframe += sizeof(struct ieee80211_hdr_3addr); |
| pattrib->pktlen = sizeof(struct ieee80211_hdr_3addr); |
| |
| le_tmp = cpu_to_le16(reason); |
| pframe = rtw_set_fixed_ie(pframe, _RSON_CODE_, &le_tmp, |
| &pattrib->pktlen); |
| |
| pattrib->last_txcmdsz = pattrib->pktlen; |
| |
| if (wait_ack) { |
| ret = dump_mgntframe_and_wait_ack(padapter, pmgntframe); |
| } else { |
| dump_mgntframe(padapter, pmgntframe); |
| ret = _SUCCESS; |
| } |
| |
| exit: |
| return ret; |
| } |
| |
| int issue_deauth(struct adapter *padapter, unsigned char *da, |
| unsigned short reason) |
| { |
| DBG_88E("%s to %pM\n", __func__, da); |
| return _issue_deauth(padapter, da, reason, false); |
| } |
| |
| static int issue_deauth_ex(struct adapter *padapter, u8 *da, |
| unsigned short reason, int try_cnt, |
| int wait_ms) |
| { |
| int ret; |
| int i = 0; |
| unsigned long start = jiffies; |
| |
| do { |
| ret = _issue_deauth(padapter, da, reason, wait_ms > 0); |
| |
| i++; |
| |
| if (padapter->bDriverStopped || padapter->bSurpriseRemoved) |
| break; |
| |
| if (i < try_cnt && wait_ms > 0 && ret == _FAIL) |
| mdelay(wait_ms); |
| } while ((i < try_cnt) && ((ret == _FAIL) || (wait_ms == 0))); |
| |
| if (ret != _FAIL) { |
| ret = _SUCCESS; |
| goto exit; |
| } |
| |
| if (try_cnt && wait_ms) { |
| if (da) |
| DBG_88E(FUNC_ADPT_FMT" to %pM, ch:%u%s, %d/%d in %u ms\n", |
| FUNC_ADPT_ARG(padapter), da, rtw_get_oper_ch(padapter), |
| ret == _SUCCESS ? ", acked" : "", i, try_cnt, |
| jiffies_to_msecs(jiffies - start)); |
| else |
| DBG_88E(FUNC_ADPT_FMT", ch:%u%s, %d/%d in %u ms\n", |
| FUNC_ADPT_ARG(padapter), rtw_get_oper_ch(padapter), |
| ret == _SUCCESS ? ", acked" : "", i, try_cnt, |
| jiffies_to_msecs(jiffies - start)); |
| } |
| exit: |
| return ret; |
| } |
| |
| static void issue_action_BA(struct adapter *padapter, unsigned char *raddr, |
| unsigned char action, unsigned short status) |
| { |
| u8 category = RTW_WLAN_CATEGORY_BACK; |
| u16 start_seq; |
| u16 BA_para_set; |
| u16 reason_code; |
| u16 BA_timeout_value; |
| __le16 le_tmp; |
| u16 BA_starting_seqctrl = 0; |
| enum ht_cap_ampdu_factor max_rx_ampdu_factor; |
| struct xmit_frame *pmgntframe; |
| struct pkt_attrib *pattrib; |
| u8 *pframe; |
| struct ieee80211_hdr *pwlanhdr; |
| __le16 *fctrl; |
| struct xmit_priv *pxmitpriv = &padapter->xmitpriv; |
| struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv; |
| struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info; |
| struct sta_info *psta; |
| struct sta_priv *pstapriv = &padapter->stapriv; |
| struct registry_priv *pregpriv = &padapter->registrypriv; |
| struct wlan_bssid_ex *pnetwork = &pmlmeinfo->network; |
| |
| DBG_88E("%s, category=%d, action=%d, status=%d\n", __func__, category, action, status); |
| |
| pmgntframe = alloc_mgtxmitframe(pxmitpriv); |
| if (!pmgntframe) |
| return; |
| |
| /* update attribute */ |
| pattrib = &pmgntframe->attrib; |
| update_mgntframe_attrib(padapter, pattrib); |
| |
| memset(pmgntframe->buf_addr, 0, WLANHDR_OFFSET + TXDESC_OFFSET); |
| |
| pframe = (u8 *)(pmgntframe->buf_addr) + TXDESC_OFFSET; |
| pwlanhdr = (struct ieee80211_hdr *)pframe; |
| |
| fctrl = &pwlanhdr->frame_control; |
| *(fctrl) = 0; |
| |
| ether_addr_copy(pwlanhdr->addr1, raddr); |
| ether_addr_copy(pwlanhdr->addr2, myid(&padapter->eeprompriv)); |
| ether_addr_copy(pwlanhdr->addr3, pnetwork->MacAddress); |
| |
| SetSeqNum(pwlanhdr, pmlmeext->mgnt_seq); |
| pmlmeext->mgnt_seq++; |
| SetFrameSubType(pframe, WIFI_ACTION); |
| |
| pframe += sizeof(struct ieee80211_hdr_3addr); |
| pattrib->pktlen = sizeof(struct ieee80211_hdr_3addr); |
| |
| pframe = rtw_set_fixed_ie(pframe, 1, &category, &pattrib->pktlen); |
| pframe = rtw_set_fixed_ie(pframe, 1, &action, &pattrib->pktlen); |
| |
| if (category == 3) { |
| switch (action) { |
| case 0: /* ADDBA req */ |
| do { |
| pmlmeinfo->dialogToken++; |
| } while (pmlmeinfo->dialogToken == 0); |
| pframe = rtw_set_fixed_ie(pframe, 1, &pmlmeinfo->dialogToken, &pattrib->pktlen); |
| |
| BA_para_set = 0x1002 | ((status & 0xf) << 2); /* immediate ack & 64 buffer size */ |
| le_tmp = cpu_to_le16(BA_para_set); |
| pframe = rtw_set_fixed_ie(pframe, 2, &(le_tmp), |
| &pattrib->pktlen); |
| |
| BA_timeout_value = 5000;/* 5ms */ |
| le_tmp = cpu_to_le16(BA_timeout_value); |
| pframe = rtw_set_fixed_ie(pframe, 2, &(le_tmp), |
| &pattrib->pktlen); |
| |
| psta = rtw_get_stainfo(pstapriv, raddr); |
| if (psta) { |
| start_seq = (psta->sta_xmitpriv.txseq_tid[status & 0x07] & 0xfff) + 1; |
| |
| DBG_88E("BA_starting_seqctrl=%d for TID=%d\n", start_seq, status & 0x07); |
| |
| psta->BA_starting_seqctrl[status & 0x07] = start_seq; |
| |
| BA_starting_seqctrl = start_seq << 4; |
| } |
| le_tmp = cpu_to_le16(BA_starting_seqctrl); |
| pframe = rtw_set_fixed_ie(pframe, 2, &(le_tmp), |
| &pattrib->pktlen); |
| break; |
| case 1: /* ADDBA rsp */ |
| { |
| struct ADDBA_request *ADDBA_req = &pmlmeinfo->ADDBA_req; |
| |
| pframe = rtw_set_fixed_ie(pframe, 1, |
| &ADDBA_req->dialog_token, |
| &pattrib->pktlen); |
| pframe = rtw_set_fixed_ie(pframe, 2, &status, |
| &pattrib->pktlen); |
| |
| BA_para_set = le16_to_cpu(ADDBA_req->BA_para_set) & |
| 0x3f; |
| rtw_hal_get_def_var(padapter, HW_VAR_MAX_RX_AMPDU_FACTOR, &max_rx_ampdu_factor); |
| switch (max_rx_ampdu_factor) { |
| case MAX_AMPDU_FACTOR_64K: |
| BA_para_set |= 0x1000; /* 64 buffer size */ |
| break; |
| case MAX_AMPDU_FACTOR_32K: |
| BA_para_set |= 0x0800; /* 32 buffer size */ |
| break; |
| case MAX_AMPDU_FACTOR_16K: |
| BA_para_set |= 0x0400; /* 16 buffer size */ |
| break; |
| case MAX_AMPDU_FACTOR_8K: |
| BA_para_set |= 0x0200; /* 8 buffer size */ |
| break; |
| default: |
| BA_para_set |= 0x1000; /* 64 buffer size */ |
| break; |
| } |
| |
| if (pregpriv->ampdu_amsdu == 0)/* disabled */ |
| BA_para_set = BA_para_set & ~BIT(0); |
| else if (pregpriv->ampdu_amsdu == 1)/* enabled */ |
| BA_para_set = BA_para_set | BIT(0); |
| le_tmp = cpu_to_le16(BA_para_set); |
| |
| pframe = rtw_set_fixed_ie(pframe, 2, &(le_tmp), |
| &pattrib->pktlen); |
| pframe = rtw_set_fixed_ie(pframe, 2, |
| &ADDBA_req->BA_timeout_value, |
| &pattrib->pktlen); |
| break; |
| } |
| case 2:/* DELBA */ |
| BA_para_set = (status & 0x1F) << 3; |
| le_tmp = cpu_to_le16(BA_para_set); |
| pframe = rtw_set_fixed_ie(pframe, 2, &(le_tmp), |
| &pattrib->pktlen); |
| |
| reason_code = 37;/* Requested from peer STA as it does not want to use the mechanism */ |
| le_tmp = cpu_to_le16(reason_code); |
| pframe = rtw_set_fixed_ie(pframe, 2, &(le_tmp), |
| &pattrib->pktlen); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| pattrib->last_txcmdsz = pattrib->pktlen; |
| |
| dump_mgntframe(padapter, pmgntframe); |
| } |
| |
| static void issue_action_BSSCoexistPacket(struct adapter *padapter) |
| { |
| struct list_head *plist, *phead; |
| unsigned char category, action; |
| struct xmit_frame *pmgntframe; |
| struct pkt_attrib *pattrib; |
| unsigned char *pframe; |
| struct ieee80211_hdr *pwlanhdr; |
| __le16 *fctrl; |
| struct wlan_network *pnetwork = NULL; |
| struct xmit_priv *pxmitpriv = &padapter->xmitpriv; |
| struct mlme_priv *pmlmepriv = &padapter->mlmepriv; |
| struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv; |
| struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info; |
| struct __queue *queue = &pmlmepriv->scanned_queue; |
| u8 InfoContent[16] = {0}; |
| u8 ICS[8][15]; |
| struct wlan_bssid_ex *cur_network = &pmlmeinfo->network; |
| |
| if ((pmlmepriv->num_FortyMHzIntolerant == 0) || (pmlmepriv->num_sta_no_ht == 0)) |
| return; |
| |
| if (pmlmeinfo->bwmode_updated) |
| return; |
| |
| DBG_88E("%s\n", __func__); |
| |
| category = RTW_WLAN_CATEGORY_PUBLIC; |
| action = ACT_PUBLIC_BSSCOEXIST; |
| |
| pmgntframe = alloc_mgtxmitframe(pxmitpriv); |
| if (!pmgntframe) |
| return; |
| |
| /* update attribute */ |
| pattrib = &pmgntframe->attrib; |
| update_mgntframe_attrib(padapter, pattrib); |
| |
| memset(pmgntframe->buf_addr, 0, WLANHDR_OFFSET + TXDESC_OFFSET); |
| |
| pframe = (u8 *)(pmgntframe->buf_addr) + TXDESC_OFFSET; |
| pwlanhdr = (struct ieee80211_hdr *)pframe; |
| |
| fctrl = &pwlanhdr->frame_control; |
| *(fctrl) = 0; |
| |
| ether_addr_copy(pwlanhdr->addr1, cur_network->MacAddress); |
| ether_addr_copy(pwlanhdr->addr2, myid(&padapter->eeprompriv)); |
| ether_addr_copy(pwlanhdr->addr3, cur_network->MacAddress); |
| |
| SetSeqNum(pwlanhdr, pmlmeext->mgnt_seq); |
| pmlmeext->mgnt_seq++; |
| SetFrameSubType(pframe, WIFI_ACTION); |
| |
| pframe += sizeof(struct ieee80211_hdr_3addr); |
| pattrib->pktlen = sizeof(struct ieee80211_hdr_3addr); |
| |
| pframe = rtw_set_fixed_ie(pframe, 1, &category, &pattrib->pktlen); |
| pframe = rtw_set_fixed_ie(pframe, 1, &action, &pattrib->pktlen); |
| |
| /* */ |
| if (pmlmepriv->num_FortyMHzIntolerant > 0) { |
| u8 iedata = 0; |
| |
| iedata |= BIT(2);/* 20 MHz BSS Width Request */ |
| |
| pframe = rtw_set_ie(pframe, EID_BSSCoexistence, 1, &iedata, &pattrib->pktlen); |
| } |
| |
| /* */ |
| memset(ICS, 0, sizeof(ICS)); |
| if (pmlmepriv->num_sta_no_ht > 0) { |
| int i; |
| |
| spin_lock_bh(&pmlmepriv->scanned_queue.lock); |
| |
| phead = get_list_head(queue); |
| plist = phead->next; |
| |
| while (phead != plist) { |
| uint len; |
| u8 *p; |
| struct wlan_bssid_ex *pbss_network; |
| |
| pnetwork = container_of(plist, struct wlan_network, list); |
| |
| plist = plist->next; |
| |
| pbss_network = (struct wlan_bssid_ex *)&pnetwork->network; |
| |
| p = rtw_get_ie(pbss_network->ies + _FIXED_IE_LENGTH_, _HT_CAPABILITY_IE_, &len, pbss_network->ie_length - _FIXED_IE_LENGTH_); |
| if ((p == NULL) || (len == 0)) { /* non-HT */ |
| if ((pbss_network->Configuration.DSConfig <= 0) || (pbss_network->Configuration.DSConfig > 14)) |
| continue; |
| |
| ICS[0][pbss_network->Configuration.DSConfig] = 1; |
| |
| if (ICS[0][0] == 0) |
| ICS[0][0] = 1; |
| } |
| } |
| spin_unlock_bh(&pmlmepriv->scanned_queue.lock); |
| |
| for (i = 0; i < 8; i++) { |
| if (ICS[i][0] == 1) { |
| int j, k = 0; |
| |
| InfoContent[k] = i; |
| /* SET_BSS_INTOLERANT_ELE_REG_CLASS(InfoContent, i); */ |
| k++; |
| |
| for (j = 1; j <= 14; j++) { |
| if (ICS[i][j] == 1) { |
| if (k < 16) { |
| InfoContent[k] = j; /* channel number */ |
| /* SET_BSS_INTOLERANT_ELE_CHANNEL(InfoContent+k, j); */ |
| k++; |
| } |
| } |
| } |
| |
| pframe = rtw_set_ie(pframe, EID_BSSIntolerantChlReport, k, InfoContent, &pattrib->pktlen); |
| } |
| } |
| } |
| |
| pattrib->last_txcmdsz = pattrib->pktlen; |
| |
| dump_mgntframe(padapter, pmgntframe); |
| } |
| |
| unsigned int send_delba(struct adapter *padapter, u8 initiator, u8 *addr) |
| { |
| struct sta_priv *pstapriv = &padapter->stapriv; |
| struct sta_info *psta = NULL; |
| struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv; |
| struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info; |
| u16 tid; |
| |
| if ((pmlmeinfo->state & 0x03) != WIFI_FW_AP_STATE) |
| if (!(pmlmeinfo->state & WIFI_FW_ASSOC_SUCCESS)) |
| return _SUCCESS; |
| |
| psta = rtw_get_stainfo(pstapriv, addr); |
| if (psta == NULL) |
| return _SUCCESS; |
| |
| if (initiator == 0) { /* recipient */ |
| for (tid = 0; tid < MAXTID; tid++) { |
| if (psta->recvreorder_ctrl[tid].enable) { |
| DBG_88E("rx agg disable tid(%d)\n", tid); |
| issue_action_BA(padapter, addr, RTW_WLAN_ACTION_DELBA, (((tid << 1) | initiator) & 0x1F)); |
| psta->recvreorder_ctrl[tid].enable = false; |
| psta->recvreorder_ctrl[tid].indicate_seq = 0xffff; |
| } |
| } |
| } else if (initiator == 1) { /* originator */ |
| for (tid = 0; tid < MAXTID; tid++) { |
| if (psta->htpriv.agg_enable_bitmap & BIT(tid)) { |
| DBG_88E("tx agg disable tid(%d)\n", tid); |
| issue_action_BA(padapter, addr, RTW_WLAN_ACTION_DELBA, (((tid << 1) | initiator) & 0x1F)); |
| psta->htpriv.agg_enable_bitmap &= ~BIT(tid); |
| psta->htpriv.candidate_tid_bitmap &= ~BIT(tid); |
| } |
| } |
| } |
| |
| return _SUCCESS; |
| } |
| |
| unsigned int send_beacon(struct adapter *padapter) |
| { |
| u8 bxmitok = false; |
| int issue = 0; |
| int poll = 0; |
| unsigned long start = jiffies; |
| |
| rtw_hal_set_hwreg(padapter, HW_VAR_BCN_VALID, NULL); |
| do { |
| issue_beacon(padapter, 100); |
| issue++; |
| do { |
| yield(); |
| rtw_hal_get_hwreg(padapter, HW_VAR_BCN_VALID, (u8 *)(&bxmitok)); |
| poll++; |
| } while ((poll%10) != 0 && !bxmitok && !padapter->bSurpriseRemoved && !padapter->bDriverStopped); |
| } while (!bxmitok && issue < 100 && !padapter->bSurpriseRemoved && !padapter->bDriverStopped); |
| |
| if (padapter->bSurpriseRemoved || padapter->bDriverStopped) |
| return _FAIL; |
| if (!bxmitok) { |
| DBG_88E("%s fail! %u ms\n", __func__, |
| jiffies_to_msecs(jiffies - start)); |
| return _FAIL; |
| } else { |
| u32 passing_time = jiffies_to_msecs(jiffies - start); |
| |
| if (passing_time > 100 || issue > 3) |
| DBG_88E("%s success, issue:%d, poll:%d, %u ms\n", |
| __func__, issue, poll, |
| jiffies_to_msecs(jiffies - start)); |
| return _SUCCESS; |
| } |
| } |
| |
| /**************************************************************************** |
| |
| Following are some utility functions for WiFi MLME |
| |
| *****************************************************************************/ |
| |
| static void site_survey(struct adapter *padapter) |
| { |
| unsigned char survey_channel = 0, val8; |
| enum rt_scan_type ScanType = SCAN_PASSIVE; |
| struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv; |
| struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info; |
| u32 initialgain = 0; |
| struct rtw_ieee80211_channel *ch; |
| |
| if (pmlmeext->sitesurvey_res.channel_idx < pmlmeext->sitesurvey_res.ch_num) { |
| ch = &pmlmeext->sitesurvey_res.ch[pmlmeext->sitesurvey_res.channel_idx]; |
| survey_channel = ch->hw_value; |
| ScanType = (ch->flags & RTW_IEEE80211_CHAN_PASSIVE_SCAN) ? SCAN_PASSIVE : SCAN_ACTIVE; |
| } |
| |
| if (survey_channel != 0) { |
| /* PAUSE 4-AC Queue when site_survey */ |
| /* rtw_hal_get_hwreg(padapter, HW_VAR_TXPAUSE, (u8 *)(&val8)); */ |
| /* val8 |= 0x0f; */ |
| /* rtw_hal_set_hwreg(padapter, HW_VAR_TXPAUSE, (u8 *)(&val8)); */ |
| if (pmlmeext->sitesurvey_res.channel_idx == 0) |
| set_channel_bwmode(padapter, survey_channel, HAL_PRIME_CHNL_OFFSET_DONT_CARE, HT_CHANNEL_WIDTH_20); |
| else |
| SelectChannel(padapter, survey_channel); |
| |
| if (ScanType == SCAN_ACTIVE) { /* obey the channel plan setting... */ |
| int i; |
| |
| for (i = 0; i < RTW_SSID_SCAN_AMOUNT; i++) { |
| if (pmlmeext->sitesurvey_res.ssid[i].ssid_length) { |
| /* todo: to issue two probe req??? */ |
| issue_probereq(padapter, |
| &(pmlmeext->sitesurvey_res.ssid[i]), |
| NULL, false); |
| /* msleep(SURVEY_TO>>1); */ |
| issue_probereq(padapter, |
| &(pmlmeext->sitesurvey_res.ssid[i]), |
| NULL, false); |
| } |
| } |
| |
| if (pmlmeext->sitesurvey_res.scan_mode == SCAN_ACTIVE) { |
| /* todo: to issue two probe req??? */ |
| issue_probereq(padapter, NULL, NULL, false); |
| /* msleep(SURVEY_TO>>1); */ |
| issue_probereq(padapter, NULL, NULL, false); |
| } |
| |
| if (pmlmeext->sitesurvey_res.scan_mode == SCAN_ACTIVE) { |
| /* todo: to issue two probe req??? */ |
| issue_probereq(padapter, NULL, NULL, false); |
| /* msleep(SURVEY_TO>>1); */ |
| issue_probereq(padapter, NULL, NULL, false); |
| } |
| } |
| |
| set_survey_timer(pmlmeext, pmlmeext->chan_scan_time); |
| } else { |
| /* 20100721:Interrupt scan operation here. */ |
| /* 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. */ |
| if (rtw_hal_antdiv_before_linked(padapter)) { |
| pmlmeext->sitesurvey_res.bss_cnt = 0; |
| pmlmeext->sitesurvey_res.channel_idx = -1; |
| pmlmeext->chan_scan_time = SURVEY_TO / 2; |
| set_survey_timer(pmlmeext, pmlmeext->chan_scan_time); |
| return; |
| } |
| |
| pmlmeext->sitesurvey_res.state = SCAN_COMPLETE; |
| |
| /* switch back to the original channel */ |
| |
| set_channel_bwmode(padapter, pmlmeext->cur_channel, pmlmeext->cur_ch_offset, pmlmeext->cur_bwmode); |
| |
| /* flush 4-AC Queue after site_survey */ |
| /* val8 = 0; */ |
| /* rtw_hal_set_hwreg(padapter, HW_VAR_TXPAUSE, (u8 *)(&val8)); */ |
| |
| /* config MSR */ |
| Set_MSR(padapter, (pmlmeinfo->state & 0x3)); |
| |
| initialgain = 0xff; /* restore RX GAIN */ |
| rtw_hal_set_hwreg(padapter, HW_VAR_INITIAL_GAIN, (u8 *)(&initialgain)); |
| /* turn on dynamic functions */ |
| Restore_DM_Func_Flag(padapter); |
| /* Switch_DM_Func(padapter, DYNAMIC_ALL_FUNC_ENABLE, true); */ |
| |
| if (is_client_associated_to_ap(padapter)) |
| issue_nulldata(padapter, NULL, 0, 3, 500); |
| |
| val8 = 0; /* survey done */ |
| rtw_hal_set_hwreg(padapter, HW_VAR_MLME_SITESURVEY, (u8 *)(&val8)); |
| |
| report_surveydone_event(padapter); |
| |
| pmlmeext->chan_scan_time = SURVEY_TO; |
| pmlmeext->sitesurvey_res.state = SCAN_DISABLE; |
| |
| issue_action_BSSCoexistPacket(padapter); |
| issue_action_BSSCoexistPacket(padapter); |
| issue_action_BSSCoexistPacket(padapter); |
| } |
| } |
| |
| /* collect bss info from Beacon and Probe request/response frames. */ |
| static u8 collect_bss_info(struct adapter *padapter, |
| struct recv_frame *precv_frame, |
| struct wlan_bssid_ex *bssid) |
| { |
| int i; |
| u32 len; |
| u8 *p; |
| u16 val16, subtype; |
| u8 *pframe = precv_frame->pkt->data; |
| u32 packet_len = precv_frame->pkt->len; |
| u8 ie_offset; |
| struct registry_priv *pregistrypriv = &padapter->registrypriv; |
| struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv; |
| struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info; |
| |
| len = packet_len - sizeof(struct ieee80211_hdr_3addr); |
| |
| if (len > MAX_IE_SZ) |
| return _FAIL; |
| |
| memset(bssid, 0, sizeof(struct wlan_bssid_ex)); |
| |
| subtype = GetFrameSubType(pframe); |
| |
| if (subtype == WIFI_BEACON) { |
| bssid->Reserved[0] = 1; |
| ie_offset = _BEACON_IE_OFFSET_; |
| } else { |
| /* FIXME : more type */ |
| if (subtype == WIFI_PROBEREQ) { |
| ie_offset = _PROBEREQ_IE_OFFSET_; |
| bssid->Reserved[0] = 2; |
| } else if (subtype == WIFI_PROBERSP) { |
| ie_offset = _PROBERSP_IE_OFFSET_; |
| bssid->Reserved[0] = 3; |
| } else { |
| bssid->Reserved[0] = 0; |
| ie_offset = _FIXED_IE_LENGTH_; |
| } |
| } |
| |
| bssid->Length = sizeof(struct wlan_bssid_ex) - MAX_IE_SZ + len; |
| |
| /* below is to copy the information element */ |
| bssid->ie_length = len; |
| memcpy(bssid->ies, (pframe + sizeof(struct ieee80211_hdr_3addr)), bssid->ie_length); |
| |
| /* get the signal strength in dBM.raw data */ |
| bssid->Rssi = precv_frame->attrib.phy_info.recvpower; |
| bssid->PhyInfo.SignalQuality = precv_frame->attrib.phy_info.SignalQuality;/* in percentage */ |
| bssid->PhyInfo.SignalStrength = precv_frame->attrib.phy_info.SignalStrength;/* in percentage */ |
| rtw_hal_get_def_var(padapter, HAL_DEF_CURRENT_ANTENNA, &bssid->PhyInfo.Optimum_antenna); |
| |
| /* checking SSID */ |
| p = rtw_get_ie(bssid->ies + ie_offset, _SSID_IE_, &len, bssid->ie_length - ie_offset); |
| if (!p) { |
| DBG_88E("marc: cannot find SSID for survey event\n"); |
| return _FAIL; |
| } |
| |
| if (len) { |
| if (len > NDIS_802_11_LENGTH_SSID) { |
| DBG_88E("%s()-%d: IE too long (%d) for survey event\n", __func__, __LINE__, len); |
| return _FAIL; |
| } |
| memcpy(bssid->ssid.ssid, (p + 2), len); |
| bssid->ssid.ssid_length = len; |
| } else { |
| bssid->ssid.ssid_length = 0; |
| } |
| |
| memset(bssid->SupportedRates, 0, NDIS_802_11_LENGTH_RATES_EX); |
| |
| /* checking rate info... */ |
| i = 0; |
| p = rtw_get_ie(bssid->ies + ie_offset, _SUPPORTEDRATES_IE_, &len, bssid->ie_length - ie_offset); |
| if (p != NULL) { |
| if (len > NDIS_802_11_LENGTH_RATES_EX) { |
| DBG_88E("%s()-%d: IE too long (%d) for survey event\n", __func__, __LINE__, len); |
| return _FAIL; |
| } |
| memcpy(bssid->SupportedRates, (p + 2), len); |
| i = len; |
| } |
| |
| p = rtw_get_ie(bssid->ies + ie_offset, _EXT_SUPPORTEDRATES_IE_, &len, bssid->ie_length - ie_offset); |
| if (p) { |
| if (len > (NDIS_802_11_LENGTH_RATES_EX-i)) { |
| DBG_88E("%s()-%d: IE too long (%d) for survey event\n", __func__, __LINE__, len); |
| return _FAIL; |
| } |
| memcpy(bssid->SupportedRates + i, (p + 2), len); |
| } |
| |
| /* todo: */ |
| bssid->NetworkTypeInUse = Ndis802_11OFDM24; |
| |
| if (bssid->ie_length < 12) |
| return _FAIL; |
| |
| /* Checking for DSConfig */ |
| p = rtw_get_ie(bssid->ies + ie_offset, _DSSET_IE_, &len, bssid->ie_length - ie_offset); |
| |
| bssid->Configuration.DSConfig = 0; |
| bssid->Configuration.Length = 0; |
| |
| if (p) { |
| bssid->Configuration.DSConfig = *(p + 2); |
| } else {/* In 5G, some ap do not have DSSET IE */ |
| /* checking HT info for channel */ |
| p = rtw_get_ie(bssid->ies + ie_offset, _HT_ADD_INFO_IE_, &len, bssid->ie_length - ie_offset); |
| if (p) { |
| struct HT_info_element *HT_info = (struct HT_info_element *)(p + 2); |
| |
| bssid->Configuration.DSConfig = HT_info->primary_channel; |
| } else { /* use current channel */ |
| bssid->Configuration.DSConfig = rtw_get_oper_ch(padapter); |
| } |
| } |
| |
| if (subtype == WIFI_PROBEREQ) { |
| /* FIXME */ |
| bssid->InfrastructureMode = Ndis802_11Infrastructure; |
| ether_addr_copy(bssid->MacAddress, GetAddr2Ptr(pframe)); |
| bssid->Privacy = 1; |
| return _SUCCESS; |
| } |
| |
| bssid->Configuration.BeaconPeriod = |
| get_unaligned_le16(rtw_get_beacon_interval_from_ie(bssid->ies)); |
| |
| val16 = rtw_get_capability((struct wlan_bssid_ex *)bssid); |
| |
| if (val16 & BIT(0)) { |
| bssid->InfrastructureMode = Ndis802_11Infrastructure; |
| ether_addr_copy(bssid->MacAddress, GetAddr2Ptr(pframe)); |
| } else { |
| bssid->InfrastructureMode = Ndis802_11IBSS; |
| ether_addr_copy(bssid->MacAddress, GetAddr3Ptr(pframe)); |
| } |
| |
| if (val16 & BIT(4)) |
| bssid->Privacy = 1; |
| else |
| bssid->Privacy = 0; |
| |
| bssid->Configuration.ATIMWindow = 0; |
| |
| /* 20/40 BSS Coexistence check */ |
| if ((pregistrypriv->wifi_spec == 1) && (!pmlmeinfo->bwmode_updated)) { |
| struct mlme_priv *pmlmepriv = &padapter->mlmepriv; |
| |
| p = rtw_get_ie(bssid->ies + ie_offset, _HT_CAPABILITY_IE_, &len, bssid->ie_length - ie_offset); |
| if (p && len > 0) { |
| struct ieee80211_ht_cap *pHT_caps = |
| (struct ieee80211_ht_cap *)(p + 2); |
| |
| if (le16_to_cpu(pHT_caps->cap_info) & BIT(14)) |
| pmlmepriv->num_FortyMHzIntolerant++; |
| } else { |
| pmlmepriv->num_sta_no_ht++; |
| } |
| } |
| |
| /* mark bss info receiving from nearby channel as SignalQuality 101 */ |
| if (bssid->Configuration.DSConfig != rtw_get_oper_ch(padapter)) |
| bssid->PhyInfo.SignalQuality = 101; |
| return _SUCCESS; |
| } |
| |
| static void start_create_ibss(struct adapter *padapter) |
| { |
| unsigned short caps; |
| u8 val8; |
| u8 join_type; |
| struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv; |
| struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info; |
| struct wlan_bssid_ex *pnetwork = (struct wlan_bssid_ex *)(&pmlmeinfo->network); |
| |
| pmlmeext->cur_channel = (u8)pnetwork->Configuration.DSConfig; |
| pmlmeinfo->bcn_interval = get_beacon_interval(pnetwork); |
| |
| /* update wireless mode */ |
| update_wireless_mode(padapter); |
| |
| /* update capability */ |
| caps = rtw_get_capability((struct wlan_bssid_ex *)pnetwork); |
| update_capinfo(padapter, caps); |
| if (caps & cap_IBSS) {/* adhoc master */ |
| val8 = 0xcf; |
| rtw_hal_set_hwreg(padapter, HW_VAR_SEC_CFG, (u8 *)(&val8)); |
| |
| /* switch channel */ |
| /* SelectChannel(padapter, pmlmeext->cur_channel, HAL_PRIME_CHNL_OFFSET_DONT_CARE); */ |
| set_channel_bwmode(padapter, pmlmeext->cur_channel, HAL_PRIME_CHNL_OFFSET_DONT_CARE, HT_CHANNEL_WIDTH_20); |
| |
| beacon_timing_control(padapter); |
| |
| /* set msr to WIFI_FW_ADHOC_STATE */ |
| pmlmeinfo->state = WIFI_FW_ADHOC_STATE; |
| Set_MSR(padapter, (pmlmeinfo->state & 0x3)); |
| |
| /* issue beacon */ |
| if (send_beacon(padapter) == _FAIL) { |
| RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_, ("issuing beacon frame fail....\n")); |
| |
| report_join_res(padapter, -1); |
| pmlmeinfo->state = WIFI_FW_NULL_STATE; |
| } else { |
| rtw_hal_set_hwreg(padapter, HW_VAR_BSSID, padapter->registrypriv.dev_network.MacAddress); |
| join_type = 0; |
| rtw_hal_set_hwreg(padapter, HW_VAR_MLME_JOIN, (u8 *)(&join_type)); |
| |
| report_join_res(padapter, 1); |
| pmlmeinfo->state |= WIFI_FW_ASSOC_SUCCESS; |
| } |
| } else { |
| DBG_88E("%s, invalid cap:%x\n", __func__, caps); |
| return; |
| } |
| } |
| |
| static void start_clnt_join(struct adapter *padapter) |
| { |
| unsigned short caps; |
| u8 val8; |
| struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv; |
| struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info; |
| struct wlan_bssid_ex *pnetwork = (struct wlan_bssid_ex *)(&pmlmeinfo->network); |
| int beacon_timeout; |
| |
| pmlmeext->cur_channel = (u8)pnetwork->Configuration.DSConfig; |
| pmlmeinfo->bcn_interval = get_beacon_interval(pnetwork); |
| |
| /* update wireless mode */ |
| update_wireless_mode(padapter); |
| |
| /* update capability */ |
| caps = rtw_get_capability((struct wlan_bssid_ex *)pnetwork); |
| update_capinfo(padapter, caps); |
| if (caps & cap_ESS) { |
| Set_MSR(padapter, WIFI_FW_STATION_STATE); |
| |
| val8 = (pmlmeinfo->auth_algo == dot11AuthAlgrthm_8021X) ? 0xcc : 0xcf; |
| |
| rtw_hal_set_hwreg(padapter, HW_VAR_SEC_CFG, (u8 *)(&val8)); |
| |
| /* switch channel */ |
| set_channel_bwmode(padapter, pmlmeext->cur_channel, pmlmeext->cur_ch_offset, pmlmeext->cur_bwmode); |
| |
| /* here wait for receiving the beacon to start auth */ |
| /* and enable a timer */ |
| beacon_timeout = decide_wait_for_beacon_timeout(pmlmeinfo->bcn_interval); |
| set_link_timer(pmlmeext, beacon_timeout); |
| mod_timer(&padapter->mlmepriv.assoc_timer, jiffies + |
| msecs_to_jiffies((REAUTH_TO * REAUTH_LIMIT) + (REASSOC_TO * REASSOC_LIMIT) + beacon_timeout)); |
| |
| pmlmeinfo->state = WIFI_FW_AUTH_NULL | WIFI_FW_STATION_STATE; |
| } else if (caps & cap_IBSS) { /* adhoc client */ |
| Set_MSR(padapter, WIFI_FW_ADHOC_STATE); |
| |
| val8 = 0xcf; |
| rtw_hal_set_hwreg(padapter, HW_VAR_SEC_CFG, (u8 *)(&val8)); |
| |
| /* switch channel */ |
| set_channel_bwmode(padapter, pmlmeext->cur_channel, pmlmeext->cur_ch_offset, pmlmeext->cur_bwmode); |
| |
| beacon_timing_control(padapter); |
| |
| pmlmeinfo->state = WIFI_FW_ADHOC_STATE; |
| |
| report_join_res(padapter, 1); |
| } else { |
| return; |
| } |
| } |
| |
| static void start_clnt_auth(struct adapter *padapter) |
| { |
| struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv; |
| struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info; |
| |
| del_timer_sync(&pmlmeext->link_timer); |
| |
| pmlmeinfo->state &= (~WIFI_FW_AUTH_NULL); |
| pmlmeinfo->state |= WIFI_FW_AUTH_STATE; |
| |
| pmlmeinfo->auth_seq = 1; |
| pmlmeinfo->reauth_count = 0; |
| pmlmeinfo->reassoc_count = 0; |
| pmlmeinfo->link_count = 0; |
| pmlmeext->retry = 0; |
| |
| /* Because of AP's not receiving deauth before */ |
| /* AP may: 1)not response auth or 2)deauth us after link is complete */ |
| /* issue deauth before issuing auth to deal with the situation */ |
| /* Commented by Albert 2012/07/21 */ |
| /* For the Win8 P2P connection, it will be hard to have a successful connection if this Wi-Fi doesn't connect to it. */ |
| issue_deauth(padapter, (&pmlmeinfo->network)->MacAddress, WLAN_REASON_DEAUTH_LEAVING); |
| |
| DBG_88E_LEVEL(_drv_info_, "start auth\n"); |
| issue_auth(padapter, NULL, 0); |
| |
| set_link_timer(pmlmeext, REAUTH_TO); |
| } |
| |
| static void start_clnt_assoc(struct adapter *padapter) |
| { |
| struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv; |
| struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info; |
| |
| del_timer_sync(&pmlmeext->link_timer); |
| |
| pmlmeinfo->state &= (~(WIFI_FW_AUTH_NULL | WIFI_FW_AUTH_STATE)); |
| pmlmeinfo->state |= (WIFI_FW_AUTH_SUCCESS | WIFI_FW_ASSOC_STATE); |
| |
| issue_assocreq(padapter); |
| |
| set_link_timer(pmlmeext, REASSOC_TO); |
| } |
| |
| static unsigned int receive_disconnect(struct adapter *padapter, |
| unsigned char *MacAddr, |
| unsigned short reason) |
| { |
| struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv; |
| struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info; |
| struct wlan_bssid_ex *pnetwork = &pmlmeinfo->network; |
| |
| /* check A3 */ |
| if (memcmp(MacAddr, pnetwork->MacAddress, ETH_ALEN)) |
| return _SUCCESS; |
| |
| DBG_88E("%s\n", __func__); |
| |
| if ((pmlmeinfo->state & 0x03) == WIFI_FW_STATION_STATE) { |
| if (pmlmeinfo->state & WIFI_FW_ASSOC_SUCCESS) { |
| pmlmeinfo->state = WIFI_FW_NULL_STATE; |
| report_del_sta_event(padapter, MacAddr, reason); |
| } else if (pmlmeinfo->state & WIFI_FW_LINKING_STATE) { |
| pmlmeinfo->state = WIFI_FW_NULL_STATE; |
| report_join_res(padapter, -2); |
| } |
| } |
| return _SUCCESS; |
| } |
| |
| static void process_80211d(struct adapter *padapter, struct wlan_bssid_ex *bssid) |
| { |
| struct registry_priv *pregistrypriv; |
| struct mlme_ext_priv *pmlmeext; |
| struct rt_channel_info *chplan_new; |
| u8 channel; |
| u8 i; |
| |
| pregistrypriv = &padapter->registrypriv; |
| pmlmeext = &padapter->mlmeextpriv; |
| |
| /* Adjust channel plan by AP Country IE */ |
| if (pregistrypriv->enable80211d && |
| (!pmlmeext->update_channel_plan_by_ap_done)) { |
| u8 *ie, *p; |
| u32 len; |
| struct rt_channel_plan chplan_ap; |
| struct rt_channel_info chplan_sta[MAX_CHANNEL_NUM]; |
| u8 country[4]; |
| u8 fcn; /* first channel number */ |
| u8 noc; /* number of channel */ |
| u8 j, k; |
| |
| ie = rtw_get_ie(bssid->ies + _FIXED_IE_LENGTH_, _COUNTRY_IE_, &len, bssid->ie_length - _FIXED_IE_LENGTH_); |
| if (!ie) |
| return; |
| if (len < 6) |
| return; |
| ie += 2; |
| p = ie; |
| ie += len; |
| |
| memset(country, 0, 4); |
| memcpy(country, p, 3); |
| p += 3; |
| RT_TRACE(_module_rtl871x_mlme_c_, _drv_notice_, |
| ("%s: 802.11d country =%s\n", __func__, country)); |
| |
| i = 0; |
| while ((ie - p) >= 3) { |
| fcn = *(p++); |
| noc = *(p++); |
| p++; |
| |
| for (j = 0; j < noc; j++) { |
| channel = fcn + j; |
| |
| chplan_ap.Channel[i++] = channel; |
| } |
| } |
| chplan_ap.Len = i; |
| |
| memcpy(chplan_sta, pmlmeext->channel_set, sizeof(chplan_sta)); |
| |
| memset(pmlmeext->channel_set, 0, sizeof(pmlmeext->channel_set)); |
| chplan_new = pmlmeext->channel_set; |
| |
| i = 0; |
| j = 0; |
| k = 0; |
| if (pregistrypriv->wireless_mode & WIRELESS_11G) { |
| do { |
| if ((i == MAX_CHANNEL_NUM) || |
| (chplan_sta[i].ChannelNum == 0) || |
| (chplan_sta[i].ChannelNum > 14)) |
| break; |
| |
| if ((j == chplan_ap.Len) || (chplan_ap.Channel[j] > 14)) |
| break; |
| |
| if (chplan_sta[i].ChannelNum == chplan_ap.Channel[j]) { |
| chplan_new[k].ChannelNum = chplan_ap.Channel[j]; |
| chplan_new[k].ScanType = SCAN_ACTIVE; |
| i++; |
| j++; |
| k++; |
| } else if (chplan_sta[i].ChannelNum < chplan_ap.Channel[j]) { |
| chplan_new[k].ChannelNum = chplan_sta[i].ChannelNum; |
| chplan_new[k].ScanType = SCAN_PASSIVE; |
| i++; |
| k++; |
| } else if (chplan_sta[i].ChannelNum > chplan_ap.Channel[j]) { |
| chplan_new[k].ChannelNum = chplan_ap.Channel[j]; |
| chplan_new[k].ScanType = SCAN_ACTIVE; |
| j++; |
| k++; |
| } |
| } while (1); |
| |
| /* change AP not support channel to Passive scan */ |
| while ((i < MAX_CHANNEL_NUM) && |
| (chplan_sta[i].ChannelNum != 0) && |
| (chplan_sta[i].ChannelNum <= 14)) { |
| chplan_new[k].ChannelNum = chplan_sta[i].ChannelNum; |
| chplan_new[k].ScanType = SCAN_PASSIVE; |
| i++; |
| k++; |
| } |
| |
| /* add channel AP supported */ |
| while ((j < chplan_ap.Len) && (chplan_ap.Channel[j] <= 14)) { |
| chplan_new[k].ChannelNum = chplan_ap.Channel[j]; |
| chplan_new[k].ScanType = SCAN_ACTIVE; |
| j++; |
| k++; |
| } |
| } else { |
| /* keep original STA 2.4G channel plan */ |
| while ((i < MAX_CHANNEL_NUM) && |
| (chplan_sta[i].ChannelNum != 0) && |
| (chplan_sta[i].ChannelNum <= 14)) { |
| chplan_new[k].ChannelNum = chplan_sta[i].ChannelNum; |
| chplan_new[k].ScanType = chplan_sta[i].ScanType; |
| i++; |
| k++; |
| } |
| |
| /* skip AP 2.4G channel plan */ |
| while ((j < chplan_ap.Len) && (chplan_ap.Channel[j] <= 14)) |
| j++; |
| } |
| |
| pmlmeext->update_channel_plan_by_ap_done = 1; |
| } |
| |
| /* If channel is used by AP, set channel scan type to active */ |
| channel = bssid->Configuration.DSConfig; |
| chplan_new = pmlmeext->channel_set; |
| i = 0; |
| while ((i < MAX_CHANNEL_NUM) && (chplan_new[i].ChannelNum != 0)) { |
| if (chplan_new[i].ChannelNum == channel) { |
| if (chplan_new[i].ScanType == SCAN_PASSIVE) { |
| chplan_new[i].ScanType = SCAN_ACTIVE; |
| RT_TRACE(_module_rtl871x_mlme_c_, _drv_notice_, |
| ("%s: change channel %d scan type from passive to active\n", |
| __func__, channel)); |
| } |
| break; |
| } |
| i++; |
| } |
| } |
| |
| /**************************************************************************** |
| |
| Following are the callback functions for each subtype of the management frames |
| |
| *****************************************************************************/ |
| |
| static unsigned int OnProbeReq(struct adapter *padapter, |
| struct recv_frame *precv_frame) |
| { |
| unsigned int ielen; |
| unsigned char *p; |
| struct mlme_priv *pmlmepriv = &padapter->mlmepriv; |
| struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv; |
| struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info; |
| struct wlan_bssid_ex *cur = &pmlmeinfo->network; |
| u8 *pframe = precv_frame->pkt->data; |
| uint len = precv_frame->pkt->len; |
| |
| if (check_fwstate(pmlmepriv, WIFI_STATION_STATE)) |
| return _SUCCESS; |
| |
| if (!check_fwstate(pmlmepriv, _FW_LINKED) && |
| !check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE|WIFI_AP_STATE)) |
| return _SUCCESS; |
| |
| p = rtw_get_ie(pframe + WLAN_HDR_A3_LEN + _PROBEREQ_IE_OFFSET_, _SSID_IE_, &ielen, |
| len - WLAN_HDR_A3_LEN - _PROBEREQ_IE_OFFSET_); |
| |
| /* check (wildcard) SSID */ |
| if (p) { |
| if ((ielen != 0 && memcmp((void *)(p+2), (void *)cur->ssid.ssid, cur->ssid.ssid_length)) || |
| (ielen == 0 && pmlmeinfo->hidden_ssid_mode)) |
| return _SUCCESS; |
| |
| if (check_fwstate(pmlmepriv, _FW_LINKED) && |
| pmlmepriv->cur_network.join_res) |
| issue_probersp(padapter, get_sa(pframe)); |
| } |
| return _SUCCESS; |
| } |
| |
| static unsigned int OnProbeRsp(struct adapter *padapter, |
| struct recv_frame *precv_frame) |
| { |
| struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv; |
| |
| if (pmlmeext->sitesurvey_res.state == SCAN_PROCESS) { |
| report_survey_event(padapter, precv_frame); |
| return _SUCCESS; |
| } |
| |
| return _SUCCESS; |
| } |
| |
| static unsigned int OnBeacon(struct adapter *padapter, |
| struct recv_frame *precv_frame) |
| { |
| int cam_idx; |
| struct sta_info *psta; |
| struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv; |
| struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info; |
| struct mlme_priv *pmlmepriv = &padapter->mlmepriv; |
| struct sta_priv *pstapriv = &padapter->stapriv; |
| u8 *pframe = precv_frame->pkt->data; |
| uint len = precv_frame->pkt->len; |
| struct wlan_bssid_ex *pbss; |
| int ret = _SUCCESS; |
| struct wlan_bssid_ex *pnetwork = &pmlmeinfo->network; |
| |
| if (pmlmeext->sitesurvey_res.state == SCAN_PROCESS) { |
| report_survey_event(padapter, precv_frame); |
| return _SUCCESS; |
| } |
| |
| if (!memcmp(GetAddr3Ptr(pframe), pnetwork->MacAddress, ETH_ALEN)) { |
| if (pmlmeinfo->state & WIFI_FW_AUTH_NULL) { |
| /* we should update current network before auth, or some IE is wrong */ |
| pbss = (struct wlan_bssid_ex *)rtw_malloc(sizeof(struct wlan_bssid_ex)); |
| if (pbss) { |
| if (collect_bss_info(padapter, precv_frame, pbss) == _SUCCESS) { |
| update_network(&pmlmepriv->cur_network.network, pbss, padapter, true); |
| rtw_get_bcn_info(&pmlmepriv->cur_network); |
| } |
| kfree(pbss); |
| } |
| |
| /* check the vendor of the assoc AP */ |
| pmlmeinfo->assoc_AP_vendor = check_assoc_AP(pframe+sizeof(struct ieee80211_hdr_3addr), len-sizeof(struct ieee80211_hdr_3addr)); |
| |
| /* update TSF Value */ |
| update_TSF(pmlmeext, pframe, len); |
| |
| /* start auth */ |
| start_clnt_auth(padapter); |
| |
| return _SUCCESS; |
| } |
| |
| if (((pmlmeinfo->state & 0x03) == WIFI_FW_STATION_STATE) && (pmlmeinfo->state & WIFI_FW_ASSOC_SUCCESS)) { |
| psta = rtw_get_stainfo(pstapriv, GetAddr2Ptr(pframe)); |
| if (psta != NULL) { |
| ret = rtw_check_bcn_info(padapter, pframe, len); |
| if (!ret) { |
| DBG_88E_LEVEL(_drv_info_, "ap has changed, disconnect now\n "); |
| receive_disconnect(padapter, pmlmeinfo->network.MacAddress, 65535); |
| return _SUCCESS; |
| } |
| /* update WMM, ERP in the beacon */ |
| /* todo: the timer is used instead of the number of the beacon received */ |
| if ((sta_rx_pkts(psta) & 0xf) == 0) |
| update_beacon_info(padapter, pframe, len, psta); |
| } |
| } else if ((pmlmeinfo->state & 0x03) == WIFI_FW_ADHOC_STATE) { |
| psta = rtw_get_stainfo(pstapriv, GetAddr2Ptr(pframe)); |
| if (psta != NULL) { |
| /* update WMM, ERP in the beacon */ |
| /* todo: the timer is used instead of the number of the beacon received */ |
| if ((sta_rx_pkts(psta) & 0xf) == 0) |
| update_beacon_info(padapter, pframe, len, psta); |
| } else { |
| /* allocate a new CAM entry for IBSS station */ |
| cam_idx = allocate_fw_sta_entry(padapter); |
| if (cam_idx == NUM_STA) |
| goto _END_ONBEACON_; |
| |
| /* get supported rate */ |
| if (update_sta_support_rate(padapter, (pframe + WLAN_HDR_A3_LEN + _BEACON_IE_OFFSET_), (len - WLAN_HDR_A3_LEN - _BEACON_IE_OFFSET_), cam_idx) == _FAIL) { |
| pmlmeinfo->FW_sta_info[cam_idx].status = 0; |
| goto _END_ONBEACON_; |
| } |
| |
| /* update TSF Value */ |
| update_TSF(pmlmeext, pframe, len); |
| |
| /* report sta add event */ |
| report_add_sta_event(padapter, GetAddr2Ptr(pframe), cam_idx); |
| } |
| } |
| } |
| |
| _END_ONBEACON_: |
| |
| return _SUCCESS; |
| } |
| |
| #ifdef CONFIG_88EU_AP_MODE |
| static unsigned int OnAuth(struct adapter *padapter, |
| struct recv_frame *precv_frame) |
| { |
| unsigned int auth_mode, ie_len; |
| u16 seq; |
| unsigned char *sa, *p; |
| u16 algorithm; |
| int status; |
| static struct sta_info stat; |
| struct sta_info *pstat = NULL; |
| struct sta_priv *pstapriv = &padapter->stapriv; |
| struct security_priv *psecuritypriv = &padapter->securitypriv; |
| struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv; |
| struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info; |
| u8 *pframe = precv_frame->pkt->data; |
| uint len = precv_frame->pkt->len; |
| |
| if ((pmlmeinfo->state & 0x03) != WIFI_FW_AP_STATE) |
| return _FAIL; |
| |
| DBG_88E("+%s\n", __func__); |
| |
| sa = GetAddr2Ptr(pframe); |
| |
| auth_mode = psecuritypriv->dot11AuthAlgrthm; |
| seq = le16_to_cpu(*(__le16 *)((size_t)pframe + WLAN_HDR_A3_LEN + 2)); |
| algorithm = le16_to_cpu(*(__le16 *)((size_t)pframe + WLAN_HDR_A3_LEN)); |
| |
| DBG_88E("auth alg=%x, seq=%X\n", algorithm, seq); |
| |
| if (auth_mode == 2 && psecuritypriv->dot11PrivacyAlgrthm != _WEP40_ && |
| psecuritypriv->dot11PrivacyAlgrthm != _WEP104_) |
| auth_mode = 0; |
| |
| if ((algorithm > 0 && auth_mode == 0) || /* rx a shared-key auth but shared not enabled */ |
| (algorithm == 0 && auth_mode == 1)) { /* rx a open-system auth but shared-key is enabled */ |
| DBG_88E("auth rejected due to bad alg [alg=%d, auth_mib=%d] %02X%02X%02X%02X%02X%02X\n", |
| algorithm, auth_mode, sa[0], sa[1], sa[2], sa[3], sa[4], sa[5]); |
| |
| status = _STATS_NO_SUPP_ALG_; |
| |
| goto auth_fail; |
| } |
| |
| if (!rtw_access_ctrl(padapter, sa)) { |
| status = _STATS_UNABLE_HANDLE_STA_; |
| goto auth_fail; |
| } |
| |
| pstat = rtw_get_stainfo(pstapriv, sa); |
| if (!pstat) { |
| /* allocate a new one */ |
| DBG_88E("going to alloc stainfo for sa=%pM\n", sa); |
| pstat = rtw_alloc_stainfo(pstapriv, sa); |
| if (!pstat) { |
| DBG_88E(" Exceed the upper limit of supported clients...\n"); |
| status = _STATS_UNABLE_HANDLE_STA_; |
| goto auth_fail; |
| } |
| |
| pstat->state = WIFI_FW_AUTH_NULL; |
| pstat->auth_seq = 0; |
| } else { |
| spin_lock_bh(&pstapriv->asoc_list_lock); |
| if (!list_empty(&pstat->asoc_list)) { |
| list_del_init(&pstat->asoc_list); |
| pstapriv->asoc_list_cnt--; |
| } |
| spin_unlock_bh(&pstapriv->asoc_list_lock); |
| |
| if (seq == 1) { |
| /* TODO: STA re_auth and auth timeout */ |
| } |
| } |
| |
| spin_lock_bh(&pstapriv->auth_list_lock); |
| if (list_empty(&pstat->auth_list)) { |
| list_add_tail(&pstat->auth_list, &pstapriv->auth_list); |
| pstapriv->auth_list_cnt++; |
| } |
| spin_unlock_bh(&pstapriv->auth_list_lock); |
| |
| if (pstat->auth_seq == 0) |
| pstat->expire_to = pstapriv->auth_to; |
| |
| if ((pstat->auth_seq + 1) != seq) { |
| DBG_88E("(1)auth rejected because out of seq [rx_seq=%d, exp_seq=%d]!\n", |
| seq, pstat->auth_seq+1); |
| status = _STATS_OUT_OF_AUTH_SEQ_; |
| goto auth_fail; |
| } |
| |
| if (algorithm == 0 && (auth_mode == 0 || auth_mode == 2)) { |
| if (seq == 1) { |
| pstat->state &= ~WIFI_FW_AUTH_NULL; |
| pstat->state |= WIFI_FW_AUTH_SUCCESS; |
| pstat->expire_to = pstapriv->assoc_to; |
| pstat->authalg = algorithm; |
| } else { |
| DBG_88E("(2)auth rejected because out of seq [rx_seq=%d, exp_seq=%d]!\n", |
| seq, pstat->auth_seq+1); |
| status = _STATS_OUT_OF_AUTH_SEQ_; |
| goto auth_fail; |
| } |
| } else { /* shared system or auto authentication */ |
| if (seq == 1) { |
| /* prepare for the challenging txt... */ |
| |
| pstat->state &= ~WIFI_FW_AUTH_NULL; |
| pstat->state |= WIFI_FW_AUTH_STATE; |
| pstat->authalg = algorithm; |
| pstat->auth_seq = 2; |
| } else if (seq == 3) { |
| /* checking for challenging txt... */ |
| DBG_88E("checking for challenging txt...\n"); |
| |
| p = rtw_get_ie(pframe + WLAN_HDR_A3_LEN + 4 + _AUTH_IE_OFFSET_, _CHLGETXT_IE_, &ie_len, |
| len - WLAN_HDR_A3_LEN - _AUTH_IE_OFFSET_ - 4); |
| |
| if ((p == NULL) || (ie_len <= 0)) { |
| DBG_88E("auth rejected because challenge failure!(1)\n"); |
| status = _STATS_CHALLENGE_FAIL_; |
| goto auth_fail; |
| } |
| |
| if (!memcmp((void *)(p + 2), pstat->chg_txt, 128)) { |
| pstat->state &= (~WIFI_FW_AUTH_STATE); |
| pstat->state |= WIFI_FW_AUTH_SUCCESS; |
| /* challenging txt is correct... */ |
| pstat->expire_to = pstapriv->assoc_to; |
| } else { |
| DBG_88E("auth rejected because challenge failure!\n"); |
| status = _STATS_CHALLENGE_FAIL_; |
| goto auth_fail; |
| } |
| } else { |
| DBG_88E("(3)auth rejected because out of seq [rx_seq=%d, exp_seq=%d]!\n", |
| seq, pstat->auth_seq+1); |
| status = _STATS_OUT_OF_AUTH_SEQ_; |
| goto auth_fail; |
| } |
| } |
| |
| |