X-Git-Url: https://git.karo-electronics.de/?a=blobdiff_plain;f=drivers%2Fstaging%2Frtl8712%2Frtl871x_mlme.c;fp=drivers%2Fstaging%2Frtl8712%2Frtl871x_mlme.c;h=98ba7602e25035f6f131d4d5d9bf6d5f17ee991c;hb=690c12d2c8ca50e55a3f507059c780ecdb8fd83f;hp=0000000000000000000000000000000000000000;hpb=f639011b4020a8230c8d09866f6eaadcb12e8dde;p=mv-sheeva.git diff --git a/drivers/staging/rtl8712/rtl871x_mlme.c b/drivers/staging/rtl8712/rtl871x_mlme.c new file mode 100644 index 00000000000..98ba7602e25 --- /dev/null +++ b/drivers/staging/rtl8712/rtl871x_mlme.c @@ -0,0 +1,1840 @@ +/****************************************************************************** + * rtl871x_mlme.c + * + * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. + * Linux device driver for RTL8192SU + * + * 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. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * Modifications for inclusion into the Linux staging tree are + * Copyright(c) 2010 Larry Finger. All rights reserved. + * + * Contact information: + * WLAN FAE + * Larry Finger + * + ******************************************************************************/ + +#define _RTL871X_MLME_C_ + +#include "osdep_service.h" +#include "drv_types.h" +#include "recv_osdep.h" +#include "xmit_osdep.h" +#include "mlme_osdep.h" +#include "sta_info.h" +#include "wifi.h" +#include "wlan_bssdef.h" + +static void update_ht_cap(struct _adapter *padapter, u8 *pie, uint ie_len); + +static sint _init_mlme_priv(struct _adapter *padapter) +{ + sint i; + u8 *pbuf; + struct wlan_network *pnetwork; + struct mlme_priv *pmlmepriv = &padapter->mlmepriv; + + memset((u8 *)pmlmepriv, 0, sizeof(struct mlme_priv)); + pmlmepriv->nic_hdl = (u8 *)padapter; + pmlmepriv->pscanned = NULL; + pmlmepriv->fw_state = 0; + pmlmepriv->cur_network.network.InfrastructureMode = + Ndis802_11AutoUnknown; + spin_lock_init(&(pmlmepriv->lock)); + spin_lock_init(&(pmlmepriv->lock2)); + _init_queue(&(pmlmepriv->free_bss_pool)); + _init_queue(&(pmlmepriv->scanned_queue)); + set_scanned_network_val(pmlmepriv, 0); + memset(&pmlmepriv->assoc_ssid, 0, sizeof(struct ndis_802_11_ssid)); + pbuf = _malloc(MAX_BSS_CNT * (sizeof(struct wlan_network))); + if (pbuf == NULL) + return _FAIL; + pmlmepriv->free_bss_buf = pbuf; + pnetwork = (struct wlan_network *)pbuf; + for (i = 0; i < MAX_BSS_CNT; i++) { + _init_listhead(&(pnetwork->list)); + list_insert_tail(&(pnetwork->list), + &(pmlmepriv->free_bss_pool.queue)); + pnetwork++; + } + pmlmepriv->sitesurveyctrl.last_rx_pkts = 0; + pmlmepriv->sitesurveyctrl.last_tx_pkts = 0; + pmlmepriv->sitesurveyctrl.traffic_busy = false; + /* allocate DMA-able/Non-Page memory for cmd_buf and rsp_buf */ + r8712_init_mlme_timer(padapter); + return _SUCCESS; +} + +struct wlan_network *_r8712_alloc_network(struct mlme_priv *pmlmepriv) +{ + unsigned long irqL; + struct wlan_network *pnetwork; + struct __queue *free_queue = &pmlmepriv->free_bss_pool; + struct list_head *plist = NULL; + + if (_queue_empty(free_queue) == true) + return NULL; + spin_lock_irqsave(&free_queue->lock, irqL); + plist = get_next(&(free_queue->queue)); + pnetwork = LIST_CONTAINOR(plist , struct wlan_network, list); + list_delete(&pnetwork->list); + pnetwork->last_scanned = jiffies; + pmlmepriv->num_of_scanned++; + spin_unlock_irqrestore(&free_queue->lock, irqL); + return pnetwork; +} + +static void _free_network(struct mlme_priv *pmlmepriv, + struct wlan_network *pnetwork) +{ + u32 curr_time, delta_time; + unsigned long irqL; + struct __queue *free_queue = &(pmlmepriv->free_bss_pool); + + if (pnetwork == NULL) + return; + if (pnetwork->fixed == true) + return; + curr_time = jiffies; + delta_time = (curr_time - (u32)pnetwork->last_scanned) / HZ; + if (delta_time < SCANQUEUE_LIFETIME) + return; + spin_lock_irqsave(&free_queue->lock, irqL); + list_delete(&pnetwork->list); + list_insert_tail(&pnetwork->list, &free_queue->queue); + pmlmepriv->num_of_scanned--; + spin_unlock_irqrestore(&free_queue->lock, irqL); +} + +static void _free_network_nolock(struct mlme_priv *pmlmepriv, + struct wlan_network *pnetwork) +{ + struct __queue *free_queue = &pmlmepriv->free_bss_pool; + + if (pnetwork == NULL) + return; + if (pnetwork->fixed == true) + return; + list_delete(&pnetwork->list); + list_insert_tail(&pnetwork->list, get_list_head(free_queue)); + pmlmepriv->num_of_scanned--; +} + + +/* + return the wlan_network with the matching addr + Shall be calle under atomic context... + to avoid possible racing condition... +*/ +static struct wlan_network *_r8712_find_network(struct __queue *scanned_queue, + u8 *addr) +{ + unsigned long irqL; + struct list_head *phead, *plist; + struct wlan_network *pnetwork = NULL; + u8 zero_addr[ETH_ALEN] = {0, 0, 0, 0, 0, 0}; + + if (!memcmp(zero_addr, addr, ETH_ALEN)) + return NULL; + spin_lock_irqsave(&scanned_queue->lock, irqL); + phead = get_list_head(scanned_queue); + plist = get_next(phead); + while (plist != phead) { + pnetwork = LIST_CONTAINOR(plist, struct wlan_network, list); + plist = get_next(plist); + if (!memcmp(addr, pnetwork->network.MacAddress, ETH_ALEN)) + break; + } + spin_unlock_irqrestore(&scanned_queue->lock, irqL); + return pnetwork; +} + +static void _free_network_queue(struct _adapter *padapter) +{ + unsigned long irqL; + struct list_head *phead, *plist; + struct wlan_network *pnetwork; + struct mlme_priv *pmlmepriv = &padapter->mlmepriv; + struct __queue *scanned_queue = &pmlmepriv->scanned_queue; + + spin_lock_irqsave(&scanned_queue->lock, irqL); + phead = get_list_head(scanned_queue); + plist = get_next(phead); + while (end_of_queue_search(phead, plist) == false) { + pnetwork = LIST_CONTAINOR(plist, struct wlan_network, list); + plist = get_next(plist); + _free_network(pmlmepriv, pnetwork); + } + spin_unlock_irqrestore(&scanned_queue->lock, irqL); +} + +sint r8712_if_up(struct _adapter *padapter) +{ + sint res; + + if (padapter->bDriverStopped || padapter->bSurpriseRemoved || + (check_fwstate(&padapter->mlmepriv, _FW_LINKED) == false)) { + res = false; + } else + res = true; + return res; +} + +void r8712_generate_random_ibss(u8 *pibss) +{ + u32 curtime = jiffies; + + pibss[0] = 0x02; /*in ad-hoc mode bit1 must set to 1 */ + pibss[1] = 0x11; + pibss[2] = 0x87; + pibss[3] = (u8)(curtime & 0xff); + pibss[4] = (u8)((curtime>>8) & 0xff); + pibss[5] = (u8)((curtime>>16) & 0xff); +} + +uint r8712_get_ndis_wlan_bssid_ex_sz(struct ndis_wlan_bssid_ex *bss) +{ + uint t_len; + + t_len = sizeof(u32) + 6 * sizeof(unsigned long) + 2 + + sizeof(struct ndis_802_11_ssid) + sizeof(u32) + + sizeof(s32) + + sizeof(enum NDIS_802_11_NETWORK_TYPE) + + sizeof(struct NDIS_802_11_CONFIGURATION) + + sizeof(enum NDIS_802_11_NETWORK_INFRASTRUCTURE) + + sizeof(NDIS_802_11_RATES_EX) + + sizeof(u32) + bss->IELength; + return t_len; +} + +u8 *r8712_get_capability_from_ie(u8 *ie) +{ + return ie + 8 + 2; +} + +int r8712_init_mlme_priv(struct _adapter *padapter) +{ + return _init_mlme_priv(padapter); +} + +void r8712_free_mlme_priv(struct mlme_priv *pmlmepriv) +{ + kfree(pmlmepriv->free_bss_buf); +} + +static struct wlan_network *alloc_network(struct mlme_priv *pmlmepriv) +{ + return _r8712_alloc_network(pmlmepriv); +} + +static void free_network_nolock(struct mlme_priv *pmlmepriv, + struct wlan_network *pnetwork) +{ + _free_network_nolock(pmlmepriv, pnetwork); +} + +void r8712_free_network_queue(struct _adapter *dev) +{ + _free_network_queue(dev); +} + +/* + return the wlan_network with the matching addr + + Shall be calle under atomic context... + to avoid possible racing condition... +*/ +static struct wlan_network *r8712_find_network(struct __queue *scanned_queue, + u8 *addr) +{ + struct wlan_network *pnetwork = _r8712_find_network(scanned_queue, + addr); + + return pnetwork; +} + +int r8712_is_same_ibss(struct _adapter *adapter, struct wlan_network *pnetwork) +{ + int ret = true; + struct security_priv *psecuritypriv = &adapter->securitypriv; + + if ((psecuritypriv->PrivacyAlgrthm != _NO_PRIVACY_) && + (pnetwork->network.Privacy == 0)) + ret = false; + else if ((psecuritypriv->PrivacyAlgrthm == _NO_PRIVACY_) && + (pnetwork->network.Privacy == 1)) + ret = false; + else + ret = true; + return ret; + +} + +static int is_same_network(struct ndis_wlan_bssid_ex *src, + struct ndis_wlan_bssid_ex *dst) +{ + u16 s_cap, d_cap; + + memcpy((u8 *)&s_cap, r8712_get_capability_from_ie(src->IEs), 2); + memcpy((u8 *)&d_cap, r8712_get_capability_from_ie(dst->IEs), 2); + return (src->Ssid.SsidLength == dst->Ssid.SsidLength) && + (src->Configuration.DSConfig == + dst->Configuration.DSConfig) && + ((!memcmp(src->MacAddress, dst->MacAddress, + ETH_ALEN))) && + ((!memcmp(src->Ssid.Ssid, + dst->Ssid.Ssid, + src->Ssid.SsidLength))) && + ((s_cap & WLAN_CAPABILITY_IBSS) == + (d_cap & WLAN_CAPABILITY_IBSS)) && + ((s_cap & WLAN_CAPABILITY_BSS) == + (d_cap & WLAN_CAPABILITY_BSS)); + +} + +struct wlan_network *r8712_get_oldest_wlan_network( + struct __queue *scanned_queue) +{ + struct list_head *plist, *phead; + struct wlan_network *pwlan = NULL; + struct wlan_network *oldest = NULL; + + phead = get_list_head(scanned_queue); + plist = get_next(phead); + while (1) { + if (end_of_queue_search(phead, plist) == true) + break; + pwlan = LIST_CONTAINOR(plist, struct wlan_network, list); + if (pwlan->fixed != true) { + if (oldest == NULL || + time_after((unsigned long)oldest->last_scanned, + (unsigned long)pwlan->last_scanned)) + oldest = pwlan; + } + plist = get_next(plist); + } + return oldest; +} + +static void update_network(struct ndis_wlan_bssid_ex *dst, + struct ndis_wlan_bssid_ex *src, + struct _adapter *padapter) +{ + u32 last_evm = 0, tmpVal; + + if (check_fwstate(&padapter->mlmepriv, _FW_LINKED) && + is_same_network(&(padapter->mlmepriv.cur_network.network), src)) { + if (padapter->recvpriv.signal_qual_data.total_num++ >= + PHY_LINKQUALITY_SLID_WIN_MAX) { + padapter->recvpriv.signal_qual_data.total_num = + PHY_LINKQUALITY_SLID_WIN_MAX; + last_evm = padapter->recvpriv.signal_qual_data. + elements[padapter->recvpriv. + signal_qual_data.index]; + padapter->recvpriv.signal_qual_data.total_val -= + last_evm; + } + padapter->recvpriv.signal_qual_data.total_val += src->Rssi; + + padapter->recvpriv.signal_qual_data. + elements[padapter->recvpriv.signal_qual_data. + index++] = src->Rssi; + if (padapter->recvpriv.signal_qual_data.index >= + PHY_LINKQUALITY_SLID_WIN_MAX) + padapter->recvpriv.signal_qual_data.index = 0; + /* <1> Showed on UI for user, in percentage. */ + tmpVal = padapter->recvpriv.signal_qual_data.total_val / + padapter->recvpriv.signal_qual_data.total_num; + padapter->recvpriv.signal = (u8)tmpVal; + + src->Rssi = padapter->recvpriv.signal; + } else + src->Rssi = (src->Rssi + dst->Rssi) / 2; + memcpy((u8 *)dst, (u8 *)src, r8712_get_ndis_wlan_bssid_ex_sz(src)); +} + +static void update_current_network(struct _adapter *adapter, + struct ndis_wlan_bssid_ex *pnetwork) +{ + struct mlme_priv *pmlmepriv = &adapter->mlmepriv; + + if (is_same_network(&(pmlmepriv->cur_network.network), pnetwork)) { + update_network(&(pmlmepriv->cur_network.network), + pnetwork, adapter); + r8712_update_protection(adapter, + (pmlmepriv->cur_network.network.IEs) + + sizeof(struct NDIS_802_11_FIXED_IEs), + pmlmepriv->cur_network.network.IELength); + } +} + +/* +Caller must hold pmlmepriv->lock first. +*/ +static void update_scanned_network(struct _adapter *adapter, + struct ndis_wlan_bssid_ex *target) +{ + struct list_head *plist, *phead; + + u32 bssid_ex_sz; + struct mlme_priv *pmlmepriv = &adapter->mlmepriv; + struct __queue *queue = &pmlmepriv->scanned_queue; + struct wlan_network *pnetwork = NULL; + struct wlan_network *oldest = NULL; + + phead = get_list_head(queue); + plist = get_next(phead); + + while (1) { + if (end_of_queue_search(phead, plist) == true) + break; + + pnetwork = LIST_CONTAINOR(plist, struct wlan_network, list); + if (is_same_network(&pnetwork->network, target)) + break; + if ((oldest == ((struct wlan_network *)0)) || + time_after((unsigned long)oldest->last_scanned, + (unsigned long)pnetwork->last_scanned)) + oldest = pnetwork; + + 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 (end_of_queue_search(phead, plist) == true) { + if (_queue_empty(&pmlmepriv->free_bss_pool) == true) { + /* If there are no more slots, expire the oldest */ + pnetwork = oldest; + target->Rssi = (pnetwork->network.Rssi + + target->Rssi) / 2; + memcpy(&pnetwork->network, target, + r8712_get_ndis_wlan_bssid_ex_sz(target)); + pnetwork->last_scanned = jiffies; + } else { + /* Otherwise just pull from the free list */ + /* update scan_time */ + pnetwork = alloc_network(pmlmepriv); + if (pnetwork == NULL) + return; + bssid_ex_sz = r8712_get_ndis_wlan_bssid_ex_sz(target); + target->Length = bssid_ex_sz; + memcpy(&pnetwork->network, target, bssid_ex_sz); + 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 + */ + update_network(&pnetwork->network, target, adapter); + pnetwork->last_scanned = jiffies; + } +} + +static void rtl8711_add_network(struct _adapter *adapter, + struct ndis_wlan_bssid_ex *pnetwork) +{ + unsigned long irqL; + struct mlme_priv *pmlmepriv = &(((struct _adapter *)adapter)->mlmepriv); + struct __queue *queue = &pmlmepriv->scanned_queue; + + spin_lock_irqsave(&queue->lock, irqL); + update_current_network(adapter, pnetwork); + update_scanned_network(adapter, pnetwork); + spin_unlock_irqrestore(&queue->lock, irqL); +} + +/*select the desired network based on the capability of the (i)bss. + * check items: (1) security + * (2) network_type + * (3) WMM + * (4) HT + * (5) others + */ +static int is_desired_network(struct _adapter *adapter, + struct wlan_network *pnetwork) +{ + u8 wps_ie[512]; + uint wps_ielen; + int bselected = true; + struct security_priv *psecuritypriv = &adapter->securitypriv; + + if (psecuritypriv->wps_phase == true) { + if (r8712_get_wps_ie(pnetwork->network.IEs, + pnetwork->network.IELength, wps_ie, + &wps_ielen) == true) + return true; + else + return false; + } + if ((psecuritypriv->PrivacyAlgrthm != _NO_PRIVACY_) && + (pnetwork->network.Privacy == 0)) + bselected = false; + return bselected; +} + +/* TODO: Perry : For Power Management */ +void r8712_atimdone_event_callback(struct _adapter *adapter , u8 *pbuf) +{ +} + +void r8712_survey_event_callback(struct _adapter *adapter, u8 *pbuf) +{ + unsigned long flags; + u32 len; + struct ndis_wlan_bssid_ex *pnetwork; + struct mlme_priv *pmlmepriv = &adapter->mlmepriv; + + pnetwork = (struct ndis_wlan_bssid_ex *)pbuf; +#ifdef __BIG_ENDIAN + /* endian_convert */ + pnetwork->Length = le32_to_cpu(pnetwork->Length); + pnetwork->Ssid.SsidLength = le32_to_cpu(pnetwork->Ssid.SsidLength); + pnetwork->Privacy = le32_to_cpu(pnetwork->Privacy); + pnetwork->Rssi = le32_to_cpu(pnetwork->Rssi); + pnetwork->NetworkTypeInUse = le32_to_cpu(pnetwork->NetworkTypeInUse); + pnetwork->Configuration.ATIMWindow = + le32_to_cpu(pnetwork->Configuration.ATIMWindow); + pnetwork->Configuration.BeaconPeriod = + le32_to_cpu(pnetwork->Configuration.BeaconPeriod); + pnetwork->Configuration.DSConfig = + le32_to_cpu(pnetwork->Configuration.DSConfig); + pnetwork->Configuration.FHConfig.DwellTime = + le32_to_cpu(pnetwork->Configuration.FHConfig.DwellTime); + pnetwork->Configuration.FHConfig.HopPattern = + le32_to_cpu(pnetwork->Configuration.FHConfig.HopPattern); + pnetwork->Configuration.FHConfig.HopSet = + le32_to_cpu(pnetwork->Configuration.FHConfig.HopSet); + pnetwork->Configuration.FHConfig.Length = + le32_to_cpu(pnetwork->Configuration.FHConfig.Length); + pnetwork->Configuration.Length = + le32_to_cpu(pnetwork->Configuration.Length); + pnetwork->InfrastructureMode = + le32_to_cpu(pnetwork->InfrastructureMode); + pnetwork->IELength = le32_to_cpu(pnetwork->IELength); +#endif + len = r8712_get_ndis_wlan_bssid_ex_sz(pnetwork); + if (len > sizeof(struct wlan_bssid_ex)) + return; + spin_lock_irqsave(&pmlmepriv->lock2, flags); + /* update IBSS_network 's timestamp */ + if (check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE) == true) { + if (!memcmp(&(pmlmepriv->cur_network.network.MacAddress), + pnetwork->MacAddress, ETH_ALEN)) { + struct wlan_network *ibss_wlan = NULL; + + memcpy(pmlmepriv->cur_network.network.IEs, + pnetwork->IEs, 8); + ibss_wlan = r8712_find_network( + &pmlmepriv->scanned_queue, + pnetwork->MacAddress); + if (!ibss_wlan) { + memcpy(ibss_wlan->network.IEs, + pnetwork->IEs, 8); + goto exit; + } + } + } + /* lock pmlmepriv->lock when you accessing network_q */ + if (check_fwstate(pmlmepriv, _FW_UNDER_LINKING) == false) { + if (pnetwork->Ssid.Ssid[0] != 0) + rtl8711_add_network(adapter, pnetwork); + else { + pnetwork->Ssid.SsidLength = 8; + memcpy(pnetwork->Ssid.Ssid, "", 8); + rtl8711_add_network(adapter, pnetwork); + } + } +exit: + spin_unlock_irqrestore(&pmlmepriv->lock2, flags); +} + +void r8712_surveydone_event_callback(struct _adapter *adapter, u8 *pbuf) +{ + unsigned long irqL; + struct mlme_priv *pmlmepriv = &adapter->mlmepriv; + + spin_lock_irqsave(&pmlmepriv->lock, irqL); + + if (check_fwstate(pmlmepriv, _FW_UNDER_SURVEY) == true) { + u8 timer_cancelled; + + _cancel_timer(&pmlmepriv->scan_to_timer, &timer_cancelled); + + _clr_fwstate_(pmlmepriv, _FW_UNDER_SURVEY); + } + + if (pmlmepriv->to_join == true) { + if ((check_fwstate(pmlmepriv, WIFI_ADHOC_STATE) == true)) { + if (check_fwstate(pmlmepriv, _FW_LINKED) == false) { + set_fwstate(pmlmepriv, _FW_UNDER_LINKING); + + if (r8712_select_and_join_from_scan(pmlmepriv) + == _SUCCESS) + _set_timer(&pmlmepriv->assoc_timer, + MAX_JOIN_TIMEOUT); + else { + struct wlan_bssid_ex *pdev_network = + &(adapter->registrypriv.dev_network); + u8 *pibss = + adapter->registrypriv. + dev_network.MacAddress; + pmlmepriv->fw_state ^= _FW_UNDER_SURVEY; + memset(&pdev_network->Ssid, 0, + sizeof(struct + ndis_802_11_ssid)); + memcpy(&pdev_network->Ssid, + &pmlmepriv->assoc_ssid, + sizeof(struct + ndis_802_11_ssid)); + r8712_update_registrypriv_dev_network + (adapter); + r8712_generate_random_ibss(pibss); + pmlmepriv->fw_state = + WIFI_ADHOC_MASTER_STATE; + pmlmepriv->to_join = false; + } + } + } else { + pmlmepriv->to_join = false; + set_fwstate(pmlmepriv, _FW_UNDER_LINKING); + if (r8712_select_and_join_from_scan(pmlmepriv) == + _SUCCESS) + _set_timer(&pmlmepriv->assoc_timer, + MAX_JOIN_TIMEOUT); + else + _clr_fwstate_(pmlmepriv, _FW_UNDER_LINKING); + } + } + spin_unlock_irqrestore(&pmlmepriv->lock, irqL); +} + +/* + *r8712_free_assoc_resources: the caller has to lock pmlmepriv->lock + */ +void r8712_free_assoc_resources(struct _adapter *adapter) +{ + unsigned long irqL; + struct wlan_network *pwlan = NULL; + struct mlme_priv *pmlmepriv = &adapter->mlmepriv; + struct sta_priv *pstapriv = &adapter->stapriv; + struct wlan_network *tgt_network = &pmlmepriv->cur_network; + + pwlan = r8712_find_network(&pmlmepriv->scanned_queue, + tgt_network->network.MacAddress); + + if (check_fwstate(pmlmepriv, WIFI_STATION_STATE|WIFI_AP_STATE)) { + struct sta_info *psta; + + psta = r8712_get_stainfo(&adapter->stapriv, + tgt_network->network.MacAddress); + + spin_lock_irqsave(&pstapriv->sta_hash_lock, irqL); + r8712_free_stainfo(adapter, psta); + spin_unlock_irqrestore(&pstapriv->sta_hash_lock, irqL); + } + + if (check_fwstate(pmlmepriv, + WIFI_ADHOC_STATE|WIFI_ADHOC_MASTER_STATE|WIFI_AP_STATE)) + r8712_free_all_stainfo(adapter); + if (pwlan) + pwlan->fixed = false; + + if (((check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE)) && + (adapter->stapriv.asoc_sta_count == 1))) + free_network_nolock(pmlmepriv, pwlan); +} + +/* +*r8712_indicate_connect: the caller has to lock pmlmepriv->lock +*/ +void r8712_indicate_connect(struct _adapter *padapter) +{ + struct mlme_priv *pmlmepriv = &padapter->mlmepriv; + + pmlmepriv->to_join = false; + set_fwstate(pmlmepriv, _FW_LINKED); + padapter->ledpriv.LedControlHandler(padapter, LED_CTL_LINK); + r8712_os_indicate_connect(padapter); + if (padapter->registrypriv.power_mgnt > PS_MODE_ACTIVE) + _set_timer(&pmlmepriv->dhcp_timer, 60000); +} + + +/* +*r8712_ind_disconnect: the caller has to lock pmlmepriv->lock +*/ +void r8712_ind_disconnect(struct _adapter *padapter) +{ + struct mlme_priv *pmlmepriv = &padapter->mlmepriv; + + _clr_fwstate_(pmlmepriv, _FW_LINKED); + padapter->ledpriv.LedControlHandler(padapter, LED_CTL_NO_LINK); + r8712_os_indicate_disconnect(padapter); + if (padapter->pwrctrlpriv.pwr_mode != + padapter->registrypriv.power_mgnt) { + _cancel_timer_ex(&pmlmepriv->dhcp_timer); + r8712_set_ps_mode(padapter, padapter->registrypriv.power_mgnt, + padapter->registrypriv.smart_ps); + } +} + +/*Notes: + *pnetwork : returns from r8712_joinbss_event_callback + *ptarget_wlan: found from scanned_queue + *if join_res > 0, for (fw_state==WIFI_STATION_STATE), we check if + * "ptarget_sta" & "ptarget_wlan" exist. + *if join_res > 0, for (fw_state==WIFI_ADHOC_STATE), we only check + * if "ptarget_wlan" exist. + *if join_res > 0, update "cur_network->network" from + * "pnetwork->network" if (ptarget_wlan !=NULL). + */ +void r8712_joinbss_event_callback(struct _adapter *adapter, u8 *pbuf) +{ + unsigned long irqL = 0, irqL2; + u8 timer_cancelled; + struct sta_info *ptarget_sta = NULL, *pcur_sta = NULL; + struct sta_priv *pstapriv = &adapter->stapriv; + struct mlme_priv *pmlmepriv = &adapter->mlmepriv; + struct wlan_network *cur_network = &pmlmepriv->cur_network; + struct wlan_network *pcur_wlan = NULL, *ptarget_wlan = NULL; + unsigned int the_same_macaddr = false; + struct wlan_network *pnetwork; + + if (sizeof(struct list_head) == 4 * sizeof(u32)) { + pnetwork = (struct wlan_network *) + _malloc(sizeof(struct wlan_network)); + memcpy((u8 *)pnetwork+16, (u8 *)pbuf + 8, + sizeof(struct wlan_network) - 16); + } else + pnetwork = (struct wlan_network *)pbuf; + +#ifdef __BIG_ENDIAN + /* endian_convert */ + pnetwork->join_res = le32_to_cpu(pnetwork->join_res); + pnetwork->network_type = le32_to_cpu(pnetwork->network_type); + pnetwork->network.Length = le32_to_cpu(pnetwork->network.Length); + pnetwork->network.Ssid.SsidLength = + le32_to_cpu(pnetwork->network.Ssid.SsidLength); + pnetwork->network.Privacy = le32_to_cpu(pnetwork->network.Privacy); + pnetwork->network.Rssi = le32_to_cpu(pnetwork->network.Rssi); + pnetwork->network.NetworkTypeInUse = + le32_to_cpu(pnetwork->network.NetworkTypeInUse); + pnetwork->network.Configuration.ATIMWindow = + le32_to_cpu(pnetwork->network.Configuration.ATIMWindow); + pnetwork->network.Configuration.BeaconPeriod = + le32_to_cpu(pnetwork->network.Configuration.BeaconPeriod); + pnetwork->network.Configuration.DSConfig = + le32_to_cpu(pnetwork->network.Configuration.DSConfig); + pnetwork->network.Configuration.FHConfig.DwellTime = + le32_to_cpu(pnetwork->network.Configuration.FHConfig. + DwellTime); + pnetwork->network.Configuration.FHConfig.HopPattern = + le32_to_cpu(pnetwork->network.Configuration. + FHConfig.HopPattern); + pnetwork->network.Configuration.FHConfig.HopSet = + le32_to_cpu(pnetwork->network.Configuration.FHConfig.HopSet); + pnetwork->network.Configuration.FHConfig.Length = + le32_to_cpu(pnetwork->network.Configuration.FHConfig.Length); + pnetwork->network.Configuration.Length = + le32_to_cpu(pnetwork->network.Configuration.Length); + pnetwork->network.InfrastructureMode = + le32_to_cpu(pnetwork->network.InfrastructureMode); + pnetwork->network.IELength = le32_to_cpu(pnetwork->network.IELength); +#endif + + the_same_macaddr = !memcmp(pnetwork->network.MacAddress, + cur_network->network.MacAddress, ETH_ALEN); + pnetwork->network.Length = + r8712_get_ndis_wlan_bssid_ex_sz(&pnetwork->network); + spin_lock_irqsave(&pmlmepriv->lock, irqL); + if (pnetwork->network.Length > sizeof(struct wlan_bssid_ex)) + goto ignore_joinbss_callback; + if (pnetwork->join_res > 0) { + if (check_fwstate(pmlmepriv, _FW_UNDER_LINKING) == true) { + /*s1. find ptarget_wlan*/ + if (check_fwstate(pmlmepriv, _FW_LINKED) == true) { + if (the_same_macaddr == true) + ptarget_wlan = + r8712_find_network(&pmlmepriv-> + scanned_queue, + cur_network->network.MacAddress); + else { + pcur_wlan = + r8712_find_network(&pmlmepriv-> + scanned_queue, + cur_network->network.MacAddress); + pcur_wlan->fixed = false; + + pcur_sta = r8712_get_stainfo(pstapriv, + cur_network->network.MacAddress); + spin_lock_irqsave(&pstapriv-> + sta_hash_lock, irqL2); + r8712_free_stainfo(adapter, pcur_sta); + spin_unlock_irqrestore(&(pstapriv-> + sta_hash_lock), irqL2); + + ptarget_wlan = + r8712_find_network(&pmlmepriv-> + scanned_queue, + pnetwork->network. + MacAddress); + if (ptarget_wlan) + ptarget_wlan->fixed = true; + } + } else { + ptarget_wlan = r8712_find_network(&pmlmepriv-> + scanned_queue, + pnetwork->network.MacAddress); + if (ptarget_wlan) + ptarget_wlan->fixed = true; + } + + if (ptarget_wlan == NULL) { + if (check_fwstate(pmlmepriv, + _FW_UNDER_LINKING)) + pmlmepriv->fw_state ^= + _FW_UNDER_LINKING; + goto ignore_joinbss_callback; + } + + /*s2. find ptarget_sta & update ptarget_sta*/ + if (check_fwstate(pmlmepriv, WIFI_STATION_STATE)) { + if (the_same_macaddr == true) { + ptarget_sta = + r8712_get_stainfo(pstapriv, + pnetwork->network.MacAddress); + if (ptarget_sta == NULL) + ptarget_sta = + r8712_alloc_stainfo(pstapriv, + pnetwork->network.MacAddress); + } else + ptarget_sta = + r8712_alloc_stainfo(pstapriv, + pnetwork->network.MacAddress); + if (ptarget_sta) /*update ptarget_sta*/ { + ptarget_sta->aid = pnetwork->join_res; + ptarget_sta->qos_option = 1; + ptarget_sta->mac_id = 5; + if (adapter->securitypriv. + AuthAlgrthm == 2) { + adapter->securitypriv. + binstallGrpkey = + false; + adapter->securitypriv. + busetkipkey = + false; + adapter->securitypriv. + bgrpkey_handshake = + false; + ptarget_sta->ieee8021x_blocked + = true; + ptarget_sta->XPrivacy = + adapter->securitypriv. + PrivacyAlgrthm; + memset((u8 *)&ptarget_sta-> + x_UncstKey, + 0, + sizeof(union Keytype)); + memset((u8 *)&ptarget_sta-> + tkiprxmickey, + 0, + sizeof(union Keytype)); + memset((u8 *)&ptarget_sta-> + tkiptxmickey, + 0, + sizeof(union Keytype)); + memset((u8 *)&ptarget_sta-> + txpn, 0, + sizeof(union pn48)); + memset((u8 *)&ptarget_sta-> + rxpn, 0, + sizeof(union pn48)); + } + } else { + if (check_fwstate(pmlmepriv, + _FW_UNDER_LINKING)) + pmlmepriv->fw_state ^= + _FW_UNDER_LINKING; + goto ignore_joinbss_callback; + } + } + + /*s3. update cur_network & indicate connect*/ + memcpy(&cur_network->network, &pnetwork->network, + pnetwork->network.Length); + cur_network->aid = pnetwork->join_res; + /*update fw_state will clr _FW_UNDER_LINKING*/ + switch (pnetwork->network.InfrastructureMode) { + case Ndis802_11Infrastructure: + pmlmepriv->fw_state = WIFI_STATION_STATE; + break; + case Ndis802_11IBSS: + pmlmepriv->fw_state = WIFI_ADHOC_STATE; + break; + default: + pmlmepriv->fw_state = WIFI_NULL_STATE; + break; + } + r8712_update_protection(adapter, + (cur_network->network.IEs) + + sizeof(struct NDIS_802_11_FIXED_IEs), + (cur_network->network.IELength)); + /*TODO: update HT_Capability*/ + update_ht_cap(adapter, cur_network->network.IEs, + cur_network->network.IELength); + /*indicate connect*/ + if (check_fwstate(pmlmepriv, WIFI_STATION_STATE) + == true) + r8712_indicate_connect(adapter); + _cancel_timer(&pmlmepriv->assoc_timer, + &timer_cancelled); + } else + goto ignore_joinbss_callback; + } else { + if (check_fwstate(pmlmepriv, _FW_UNDER_LINKING) == true) { + _set_timer(&pmlmepriv->assoc_timer, 1); + _clr_fwstate_(pmlmepriv, _FW_UNDER_LINKING); + } + } +ignore_joinbss_callback: + spin_unlock_irqrestore(&pmlmepriv->lock, irqL); + if (sizeof(struct list_head) == 4 * sizeof(u32)) + kfree((u8 *)pnetwork); +} + +void r8712_stassoc_event_callback(struct _adapter *adapter, u8 *pbuf) +{ + unsigned long irqL; + struct sta_info *psta; + struct mlme_priv *pmlmepriv = &(adapter->mlmepriv); + struct stassoc_event *pstassoc = (struct stassoc_event *)pbuf; + + /* to do: */ + if (r8712_access_ctrl(&adapter->acl_list, pstassoc->macaddr) == false) + return; + psta = r8712_get_stainfo(&adapter->stapriv, pstassoc->macaddr); + if (psta != NULL) { + /*the sta have been in sta_info_queue => do nothing + *(between drv has received this event before and + * fw have not yet to set key to CAM_ENTRY) */ + return; + } + + psta = r8712_alloc_stainfo(&adapter->stapriv, pstassoc->macaddr); + if (psta == NULL) + return; + /* to do : init sta_info variable */ + psta->qos_option = 0; + psta->mac_id = le32_to_cpu((uint)pstassoc->cam_id); + /* psta->aid = (uint)pstassoc->cam_id; */ + + if (adapter->securitypriv.AuthAlgrthm == 2) + psta->XPrivacy = adapter->securitypriv.PrivacyAlgrthm; + psta->ieee8021x_blocked = false; + spin_lock_irqsave(&pmlmepriv->lock, irqL); + if ((check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE) == true) || + (check_fwstate(pmlmepriv, WIFI_ADHOC_STATE) == true)) { + if (adapter->stapriv.asoc_sta_count == 2) { + /* a sta + bc/mc_stainfo (not Ibss_stainfo) */ + r8712_indicate_connect(adapter); + } + } + spin_unlock_irqrestore(&pmlmepriv->lock, irqL); +} + +void r8712_stadel_event_callback(struct _adapter *adapter, u8 *pbuf) +{ + unsigned long irqL, irqL2; + struct sta_info *psta; + struct wlan_network *pwlan = NULL; + struct wlan_bssid_ex *pdev_network = NULL; + u8 *pibss = NULL; + struct mlme_priv *pmlmepriv = &adapter->mlmepriv; + struct stadel_event *pstadel = (struct stadel_event *)pbuf; + struct sta_priv *pstapriv = &adapter->stapriv; + struct wlan_network *tgt_network = &pmlmepriv->cur_network; + + spin_lock_irqsave(&pmlmepriv->lock, irqL2); + if (check_fwstate(pmlmepriv, WIFI_STATION_STATE) == true) { + r8712_ind_disconnect(adapter); + r8712_free_assoc_resources(adapter); + } + if (check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE | + WIFI_ADHOC_STATE)) { + psta = r8712_get_stainfo(&adapter->stapriv, pstadel->macaddr); + spin_lock_irqsave(&pstapriv->sta_hash_lock, irqL); + r8712_free_stainfo(adapter, psta); + spin_unlock_irqrestore(&pstapriv->sta_hash_lock, irqL); + if (adapter->stapriv.asoc_sta_count == 1) { + /*a sta + bc/mc_stainfo (not Ibss_stainfo) */ + pwlan = r8712_find_network(&pmlmepriv->scanned_queue, + tgt_network->network.MacAddress); + if (pwlan) { + pwlan->fixed = false; + free_network_nolock(pmlmepriv, pwlan); + } + /*re-create ibss*/ + pdev_network = &(adapter->registrypriv.dev_network); + pibss = adapter->registrypriv.dev_network.MacAddress; + memcpy(pdev_network, &tgt_network->network, + r8712_get_ndis_wlan_bssid_ex_sz(&tgt_network-> + network)); + memset(&pdev_network->Ssid, 0, + sizeof(struct ndis_802_11_ssid)); + memcpy(&pdev_network->Ssid, + &pmlmepriv->assoc_ssid, + sizeof(struct ndis_802_11_ssid)); + r8712_update_registrypriv_dev_network(adapter); + r8712_generate_random_ibss(pibss); + if (check_fwstate(pmlmepriv, WIFI_ADHOC_STATE)) { + _clr_fwstate_(pmlmepriv, WIFI_ADHOC_STATE); + set_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE); + } + } + } + spin_unlock_irqrestore(&pmlmepriv->lock, irqL2); +} + +void r8712_cpwm_event_callback(struct _adapter *adapter, u8 *pbuf) +{ + struct reportpwrstate_parm *preportpwrstate = + (struct reportpwrstate_parm *)pbuf; + + preportpwrstate->state |= (u8)(adapter->pwrctrlpriv.cpwm_tog + 0x80); + r8712_cpwm_int_hdl(adapter, preportpwrstate); +} + +/* When the Netgear 3500 AP is with WPA2PSK-AES mode, it will send + * the ADDBA req frame with start seq control = 0 to wifi client after + * the WPA handshake and the seqence number of following data packet + * will be 0. In this case, the Rx reorder sequence is not longer than 0 + * and the WiFi client will drop the data with seq number 0. + * So, the 8712 firmware has to inform driver with receiving the + * ADDBA-Req frame so that the driver can reset the + * sequence value of Rx reorder contorl. + */ +void r8712_got_addbareq_event_callback(struct _adapter *adapter, u8 *pbuf) +{ + struct ADDBA_Req_Report_parm *pAddbareq_pram = + (struct ADDBA_Req_Report_parm *)pbuf; + struct sta_info *psta; + struct sta_priv *pstapriv = &adapter->stapriv; + struct recv_reorder_ctrl *precvreorder_ctrl = NULL; + + printk(KERN_INFO "r8712u: [%s] mac = %pM, seq = %d, tid = %d\n", + __func__, pAddbareq_pram->MacAddress, + pAddbareq_pram->StartSeqNum, pAddbareq_pram->tid); + psta = r8712_get_stainfo(pstapriv, pAddbareq_pram->MacAddress); + if (psta) { + precvreorder_ctrl = + &psta->recvreorder_ctrl[pAddbareq_pram->tid]; + /* set the indicate_seq to 0xffff so that the rx reorder + * can store any following data packet. + */ + precvreorder_ctrl->indicate_seq = 0xffff; + } +} + +void r8712_wpspbc_event_callback(struct _adapter *adapter, u8 *pbuf) +{ + if (adapter->securitypriv.wps_hw_pbc_pressed == false) + adapter->securitypriv.wps_hw_pbc_pressed = true; +} + +void _r8712_sitesurvey_ctrl_handler(struct _adapter *adapter) +{ + struct mlme_priv *pmlmepriv = &adapter->mlmepriv; + struct sitesurvey_ctrl *psitesurveyctrl = &pmlmepriv->sitesurveyctrl; + struct registry_priv *pregistrypriv = &adapter->registrypriv; + u64 current_tx_pkts; + uint current_rx_pkts; + + current_tx_pkts = (adapter->xmitpriv.tx_pkts) - + (psitesurveyctrl->last_tx_pkts); + current_rx_pkts = (adapter->recvpriv.rx_pkts) - + (psitesurveyctrl->last_rx_pkts); + psitesurveyctrl->last_tx_pkts = adapter->xmitpriv.tx_pkts; + psitesurveyctrl->last_rx_pkts = adapter->recvpriv.rx_pkts; + if ((current_tx_pkts > pregistrypriv->busy_thresh) || + (current_rx_pkts > pregistrypriv->busy_thresh)) + psitesurveyctrl->traffic_busy = true; + else + psitesurveyctrl->traffic_busy = false; +} + +void _r8712_join_timeout_handler(struct _adapter *adapter) +{ + unsigned long irqL; + struct mlme_priv *pmlmepriv = &adapter->mlmepriv; + + if (adapter->bDriverStopped || adapter->bSurpriseRemoved) + return; + spin_lock_irqsave(&pmlmepriv->lock, irqL); + _clr_fwstate_(pmlmepriv, _FW_UNDER_LINKING); + pmlmepriv->to_join = false; + if (check_fwstate(pmlmepriv, _FW_LINKED) == true) { + r8712_os_indicate_disconnect(adapter); + _clr_fwstate_(pmlmepriv, _FW_LINKED); + } + if (adapter->pwrctrlpriv.pwr_mode != adapter->registrypriv.power_mgnt) { + r8712_set_ps_mode(adapter, adapter->registrypriv.power_mgnt, + adapter->registrypriv.smart_ps); + } + spin_unlock_irqrestore(&pmlmepriv->lock, irqL); +} + +void r8712_scan_timeout_handler (struct _adapter *adapter) +{ + unsigned long irqL; + struct mlme_priv *pmlmepriv = &adapter->mlmepriv; + + spin_lock_irqsave(&pmlmepriv->lock, irqL); + _clr_fwstate_(pmlmepriv, _FW_UNDER_SURVEY); + pmlmepriv->to_join = false; /* scan fail, so clear to_join flag */ + spin_unlock_irqrestore(&pmlmepriv->lock, irqL); +} + +void _r8712_dhcp_timeout_handler (struct _adapter *adapter) +{ + if (adapter->bDriverStopped || adapter->bSurpriseRemoved) + return; + if (adapter->pwrctrlpriv.pwr_mode != adapter->registrypriv.power_mgnt) + r8712_set_ps_mode(adapter, adapter->registrypriv.power_mgnt, + adapter->registrypriv.smart_ps); +} + +void _r8712_wdg_timeout_handler(struct _adapter *adapter) +{ + r8712_wdg_wk_cmd(adapter); +} + +int r8712_select_and_join_from_scan(struct mlme_priv *pmlmepriv) +{ + struct list_head *phead; + unsigned char *dst_ssid, *src_ssid; + struct _adapter *adapter; + struct __queue *queue = NULL; + struct wlan_network *pnetwork = NULL; + struct wlan_network *pnetwork_max_rssi = NULL; + + adapter = (struct _adapter *)pmlmepriv->nic_hdl; + queue = &pmlmepriv->scanned_queue; + phead = get_list_head(queue); + pmlmepriv->pscanned = get_next(phead); + while (1) { + if (end_of_queue_search(phead, pmlmepriv->pscanned) == true) { + if ((pmlmepriv->assoc_by_rssi == true) && + (pnetwork_max_rssi != NULL)) { + pnetwork = pnetwork_max_rssi; + goto ask_for_joinbss; + } + return _FAIL; + } + pnetwork = LIST_CONTAINOR(pmlmepriv->pscanned, + struct wlan_network, list); + if (pnetwork == NULL) + return _FAIL; + pmlmepriv->pscanned = get_next(pmlmepriv->pscanned); + if (pmlmepriv->assoc_by_bssid == true) { + dst_ssid = pnetwork->network.MacAddress; + src_ssid = pmlmepriv->assoc_bssid; + if (!memcmp(dst_ssid, src_ssid, ETH_ALEN)) { + if (check_fwstate(pmlmepriv, _FW_LINKED)) { + if (is_same_network(&pmlmepriv-> + cur_network.network, + &pnetwork->network)) { + _clr_fwstate_(pmlmepriv, + _FW_UNDER_LINKING); + /*r8712_indicate_connect again*/ + r8712_indicate_connect(adapter); + return 2; + } + r8712_disassoc_cmd(adapter); + r8712_ind_disconnect(adapter); + r8712_free_assoc_resources(adapter); + } + goto ask_for_joinbss; + } + } else if (pmlmepriv->assoc_ssid.SsidLength == 0) + goto ask_for_joinbss; + dst_ssid = pnetwork->network.Ssid.Ssid; + src_ssid = pmlmepriv->assoc_ssid.Ssid; + if ((pnetwork->network.Ssid.SsidLength == + pmlmepriv->assoc_ssid.SsidLength) && + (!memcmp(dst_ssid, src_ssid, + pmlmepriv->assoc_ssid.SsidLength))) { + if (pmlmepriv->assoc_by_rssi == true) { + /* if the ssid is the same, select the bss + * which has the max rssi*/ + if (pnetwork_max_rssi) { + if (pnetwork->network.Rssi > + pnetwork_max_rssi->network.Rssi) + pnetwork_max_rssi = pnetwork; + } else + pnetwork_max_rssi = pnetwork; + } else if (is_desired_network(adapter, pnetwork)) { + if (check_fwstate(pmlmepriv, _FW_LINKED)) { + r8712_disassoc_cmd(adapter); + r8712_free_assoc_resources(adapter); + } + goto ask_for_joinbss; + } + } + } + return _FAIL; +ask_for_joinbss: + return r8712_joinbss_cmd(adapter, pnetwork); +} + +sint r8712_set_auth(struct _adapter *adapter, + struct security_priv *psecuritypriv) +{ + struct cmd_priv *pcmdpriv = &adapter->cmdpriv; + struct cmd_obj *pcmd; + struct setauth_parm *psetauthparm; + sint ret = _SUCCESS; + + pcmd = (struct cmd_obj *)_malloc(sizeof(struct cmd_obj)); + if (pcmd == NULL) + return _FAIL; + + psetauthparm = (struct setauth_parm *)_malloc( + sizeof(struct setauth_parm)); + if (psetauthparm == NULL) { + kfree((unsigned char *)pcmd); + return _FAIL; + } + memset(psetauthparm, 0, sizeof(struct setauth_parm)); + psetauthparm->mode = (u8)psecuritypriv->AuthAlgrthm; + pcmd->cmdcode = _SetAuth_CMD_; + pcmd->parmbuf = (unsigned char *)psetauthparm; + pcmd->cmdsz = sizeof(struct setauth_parm); + pcmd->rsp = NULL; + pcmd->rspsz = 0; + _init_listhead(&pcmd->list); + r8712_enqueue_cmd(pcmdpriv, pcmd); + return ret; +} + +sint r8712_set_key(struct _adapter *adapter, + struct security_priv *psecuritypriv, + sint keyid) +{ + struct cmd_priv *pcmdpriv = &adapter->cmdpriv; + struct cmd_obj *pcmd; + struct setkey_parm *psetkeyparm; + u8 keylen; + + pcmd = (struct cmd_obj *)_malloc(sizeof(struct cmd_obj)); + if (pcmd == NULL) + return _FAIL; + psetkeyparm = (struct setkey_parm *)_malloc(sizeof(struct setkey_parm)); + if (psetkeyparm == NULL) { + kfree((unsigned char *)pcmd); + return _FAIL; + } + memset(psetkeyparm, 0, sizeof(struct setkey_parm)); + if (psecuritypriv->AuthAlgrthm == 2) { /* 802.1X */ + psetkeyparm->algorithm = + (u8)psecuritypriv->XGrpPrivacy; + } else { /* WEP */ + psetkeyparm->algorithm = + (u8)psecuritypriv->PrivacyAlgrthm; + } + psetkeyparm->keyid = (u8)keyid; + + switch (psetkeyparm->algorithm) { + case _WEP40_: + keylen = 5; + memcpy(psetkeyparm->key, + psecuritypriv->DefKey[keyid].skey, keylen); + break; + case _WEP104_: + keylen = 13; + memcpy(psetkeyparm->key, + psecuritypriv->DefKey[keyid].skey, keylen); + break; + case _TKIP_: + keylen = 16; + memcpy(psetkeyparm->key, + &psecuritypriv->XGrpKey[keyid - 1], keylen); + psetkeyparm->grpkey = 1; + break; + case _AES_: + keylen = 16; + memcpy(psetkeyparm->key, + &psecuritypriv->XGrpKey[keyid - 1], keylen); + psetkeyparm->grpkey = 1; + break; + default: + return _FAIL; + } + pcmd->cmdcode = _SetKey_CMD_; + pcmd->parmbuf = (u8 *)psetkeyparm; + pcmd->cmdsz = (sizeof(struct setkey_parm)); + pcmd->rsp = NULL; + pcmd->rspsz = 0; + _init_listhead(&pcmd->list); + r8712_enqueue_cmd(pcmdpriv, pcmd); + return _SUCCESS; +} + +/* adjust IEs for r8712_joinbss_cmd in WMM */ +int r8712_restruct_wmm_ie(struct _adapter *adapter, u8 *in_ie, u8 *out_ie, + uint in_len, uint initial_out_len) +{ + unsigned int ielength = 0; + unsigned int i, j; + + i = 12; /* after the fixed IE */ + while (i < in_len) { + ielength = initial_out_len; + if (in_ie[i] == 0xDD && in_ie[i + 2] == 0x00 && + in_ie[i + 3] == 0x50 && in_ie[i + 4] == 0xF2 && + in_ie[i + 5] == 0x02 && i + 5 < in_len) { + /*WMM element ID and OUI*/ + for (j = i; j < i + 9; j++) { + out_ie[ielength] = in_ie[j]; + ielength++; + } + out_ie[initial_out_len + 1] = 0x07; + out_ie[initial_out_len + 6] = 0x00; + out_ie[initial_out_len + 8] = 0x00; + break; + } + i += (in_ie[i + 1] + 2); /* to the next IE element */ + } + return ielength; +} + +/* + * Ported from 8185: IsInPreAuthKeyList(). + * + * Search by BSSID, + * Return Value: + * -1 :if there is no pre-auth key in the table + * >=0 :if there is pre-auth key, and return the entry id + */ +static int SecIsInPMKIDList(struct _adapter *Adapter, u8 *bssid) +{ + struct security_priv *psecuritypriv = &Adapter->securitypriv; + int i = 0; + + do { + if (psecuritypriv->PMKIDList[i].bUsed && + (!memcmp(psecuritypriv->PMKIDList[i].Bssid, + bssid, ETH_ALEN))) + break; + else + i++; + } while (i < NUM_PMKID_CACHE); + + if (i == NUM_PMKID_CACHE) { + i = -1; /* Could not find. */ + } else { + ; /* There is one Pre-Authentication Key for the + * specific BSSID. */ + } + return i; +} + +sint r8712_restruct_sec_ie(struct _adapter *adapter, u8 *in_ie, + u8 *out_ie, uint in_len) +{ + u8 authmode = 0, securitytype, match; + u8 sec_ie[255], uncst_oui[4], bkup_ie[255]; + u8 wpa_oui[4] = {0x0, 0x50, 0xf2, 0x01}; + uint ielength, cnt, remove_cnt; + int iEntry; + struct mlme_priv *pmlmepriv = &adapter->mlmepriv; + struct security_priv *psecuritypriv = &adapter->securitypriv; + uint ndisauthmode = psecuritypriv->ndisauthtype; + uint ndissecuritytype = psecuritypriv->ndisencryptstatus; + + if ((ndisauthmode == Ndis802_11AuthModeWPA) || + (ndisauthmode == Ndis802_11AuthModeWPAPSK)) { + authmode = _WPA_IE_ID_; + uncst_oui[0] = 0x0; + uncst_oui[1] = 0x50; + uncst_oui[2] = 0xf2; + } + if ((ndisauthmode == Ndis802_11AuthModeWPA2) || + (ndisauthmode == Ndis802_11AuthModeWPA2PSK)) { + authmode = _WPA2_IE_ID_; + uncst_oui[0] = 0x0; + uncst_oui[1] = 0x0f; + uncst_oui[2] = 0xac; + } + switch (ndissecuritytype) { + case Ndis802_11Encryption1Enabled: + case Ndis802_11Encryption1KeyAbsent: + securitytype = _WEP40_; + uncst_oui[3] = 0x1; + break; + case Ndis802_11Encryption2Enabled: + case Ndis802_11Encryption2KeyAbsent: + securitytype = _TKIP_; + uncst_oui[3] = 0x2; + break; + case Ndis802_11Encryption3Enabled: + case Ndis802_11Encryption3KeyAbsent: + securitytype = _AES_; + uncst_oui[3] = 0x4; + break; + default: + securitytype = _NO_PRIVACY_; + break; + } + /*Search required WPA or WPA2 IE and copy to sec_ie[] */ + cnt = 12; + match = false; + while (cnt < in_len) { + if (in_ie[cnt] == authmode) { + if ((authmode == _WPA_IE_ID_) && + (!memcmp(&in_ie[cnt+2], &wpa_oui[0], 4))) { + memcpy(&sec_ie[0], &in_ie[cnt], + in_ie[cnt + 1] + 2); + match = true; + break; + } + if (authmode == _WPA2_IE_ID_) { + memcpy(&sec_ie[0], &in_ie[cnt], + in_ie[cnt + 1] + 2); + match = true; + break; + } + if (((authmode == _WPA_IE_ID_) && + (!memcmp(&in_ie[cnt + 2], &wpa_oui[0], 4))) || + (authmode == _WPA2_IE_ID_)) + memcpy(&bkup_ie[0], &in_ie[cnt], + in_ie[cnt + 1] + 2); + } + cnt += in_ie[cnt+1] + 2; /*get next*/ + } + /*restruct WPA IE or WPA2 IE in sec_ie[] */ + if (match == true) { + if (sec_ie[0] == _WPA_IE_ID_) { + /* parsing SSN IE to select required encryption + * algorithm, and set the bc/mc encryption algorithm */ + while (true) { + /*check wpa_oui tag*/ + if (memcmp(&sec_ie[2], &wpa_oui[0], 4)) { + match = false; + break; + } + if ((sec_ie[6] != 0x01) || (sec_ie[7] != 0x0)) { + /*IE Ver error*/ + match = false; + break; + } + if (!memcmp(&sec_ie[8], &wpa_oui[0], 3)) { + /* get bc/mc encryption type (group + * key type)*/ + switch (sec_ie[11]) { + case 0x0: /*none*/ + psecuritypriv->XGrpPrivacy = + _NO_PRIVACY_; + break; + case 0x1: /*WEP_40*/ + psecuritypriv->XGrpPrivacy = + _WEP40_; + break; + case 0x2: /*TKIP*/ + psecuritypriv->XGrpPrivacy = + _TKIP_; + break; + case 0x3: /*AESCCMP*/ + case 0x4: + psecuritypriv->XGrpPrivacy = + _AES_; + break; + case 0x5: /*WEP_104*/ + psecuritypriv->XGrpPrivacy = + _WEP104_; + break; + } + } else { + match = false; + break; + } + if (sec_ie[12] == 0x01) { + /*check the unicast encryption type*/ + if (memcmp(&sec_ie[14], + &uncst_oui[0], 4)) { + match = false; + break; + + } /*else the uncst_oui is match*/ + } else { /*mixed mode, unicast_enc_type > 1*/ + /*select the uncst_oui and remove + * the other uncst_oui*/ + cnt = sec_ie[12]; + remove_cnt = (cnt-1) * 4; + sec_ie[12] = 0x01; + memcpy(&sec_ie[14], &uncst_oui[0], 4); + /*remove the other unicast suit*/ + memcpy(&sec_ie[18], + &sec_ie[18 + remove_cnt], + sec_ie[1] - 18 + 2 - + remove_cnt); + sec_ie[1] = sec_ie[1] - remove_cnt; + } + break; + } + } + if (authmode == _WPA2_IE_ID_) { + /* parsing RSN IE to select required encryption + * algorithm, and set the bc/mc encryption algorithm */ + while (true) { + if ((sec_ie[2] != 0x01) || (sec_ie[3] != 0x0)) { + /*IE Ver error*/ + match = false; + break; + } + if (!memcmp(&sec_ie[4], &uncst_oui[0], 3)) { + /*get bc/mc encryption type*/ + switch (sec_ie[7]) { + case 0x1: /*WEP_40*/ + psecuritypriv->XGrpPrivacy = + _WEP40_; + break; + case 0x2: /*TKIP*/ + psecuritypriv->XGrpPrivacy = + _TKIP_; + break; + case 0x4: /*AESWRAP*/ + psecuritypriv->XGrpPrivacy = + _AES_; + break; + case 0x5: /*WEP_104*/ + psecuritypriv->XGrpPrivacy = + _WEP104_; + break; + default: /*one*/ + psecuritypriv->XGrpPrivacy = + _NO_PRIVACY_; + break; + } + } else { + match = false; + break; + } + if (sec_ie[8] == 0x01) { + /*check the unicast encryption type*/ + if (memcmp(&sec_ie[10], + &uncst_oui[0], 4)) { + match = false; + break; + } /*else the uncst_oui is match*/ + } else { /*mixed mode, unicast_enc_type > 1*/ + /*select the uncst_oui and remove the + * other uncst_oui*/ + cnt = sec_ie[8]; + remove_cnt = (cnt-1)*4; + sec_ie[8] = 0x01; + memcpy(&sec_ie[10], &uncst_oui[0], 4); + /*remove the other unicast suit*/ + memcpy(&sec_ie[14], + &sec_ie[14 + remove_cnt], + (sec_ie[1] - 14 + 2 - + remove_cnt)); + sec_ie[1] = sec_ie[1]-remove_cnt; + } + break; + } + } + } + if ((authmode == _WPA_IE_ID_) || (authmode == _WPA2_IE_ID_)) { + /*copy fixed ie*/ + memcpy(out_ie, in_ie, 12); + ielength = 12; + /*copy RSN or SSN*/ + if (match == true) { + memcpy(&out_ie[ielength], &sec_ie[0], sec_ie[1]+2); + ielength += sec_ie[1] + 2; + if (authmode == _WPA2_IE_ID_) { + /*the Pre-Authentication bit should be zero*/ + out_ie[ielength - 1] = 0; + out_ie[ielength - 2] = 0; + } + r8712_report_sec_ie(adapter, authmode, sec_ie); + } + } else { + /*copy fixed ie only*/ + memcpy(out_ie, in_ie, 12); + ielength = 12; + if (psecuritypriv->wps_phase == true) { + memcpy(out_ie+ielength, psecuritypriv->wps_ie, + psecuritypriv->wps_ie_len); + ielength += psecuritypriv->wps_ie_len; + } + } + iEntry = SecIsInPMKIDList(adapter, pmlmepriv->assoc_bssid); + if (iEntry < 0) + return ielength; + else { + if (authmode == _WPA2_IE_ID_) { + out_ie[ielength] = 1; + ielength++; + out_ie[ielength] = 0; /*PMKID count = 0x0100*/ + ielength++; + memcpy(&out_ie[ielength], + &psecuritypriv->PMKIDList[iEntry].PMKID, 16); + ielength += 16; + out_ie[13] += 18;/*PMKID length = 2+16*/ + } + } + return ielength; +} + +void r8712_init_registrypriv_dev_network(struct _adapter *adapter) +{ + struct registry_priv *pregistrypriv = &adapter->registrypriv; + struct eeprom_priv *peepriv = &adapter->eeprompriv; + struct wlan_bssid_ex *pdev_network = &pregistrypriv->dev_network; + u8 *myhwaddr = myid(peepriv); + + memcpy(pdev_network->MacAddress, myhwaddr, ETH_ALEN); + memcpy(&pdev_network->Ssid, &pregistrypriv->ssid, + sizeof(struct ndis_802_11_ssid)); + pdev_network->Configuration.Length = + sizeof(struct NDIS_802_11_CONFIGURATION); + pdev_network->Configuration.BeaconPeriod = 100; + pdev_network->Configuration.FHConfig.Length = 0; + pdev_network->Configuration.FHConfig.HopPattern = 0; + pdev_network->Configuration.FHConfig.HopSet = 0; + pdev_network->Configuration.FHConfig.DwellTime = 0; +} + +void r8712_update_registrypriv_dev_network(struct _adapter *adapter) +{ + int sz = 0; + struct registry_priv *pregistrypriv = &adapter->registrypriv; + struct wlan_bssid_ex *pdev_network = &pregistrypriv->dev_network; + struct security_priv *psecuritypriv = &adapter->securitypriv; + struct wlan_network *cur_network = &adapter->mlmepriv.cur_network; + + pdev_network->Privacy = cpu_to_le32(psecuritypriv->PrivacyAlgrthm + > 0 ? 1 : 0) ; /* adhoc no 802.1x */ + pdev_network->Rssi = 0; + switch (pregistrypriv->wireless_mode) { + case WIRELESS_11B: + pdev_network->NetworkTypeInUse = cpu_to_le32(Ndis802_11DS); + break; + case WIRELESS_11G: + case WIRELESS_11BG: + pdev_network->NetworkTypeInUse = cpu_to_le32(Ndis802_11OFDM24); + break; + case WIRELESS_11A: + pdev_network->NetworkTypeInUse = cpu_to_le32(Ndis802_11OFDM5); + break; + default: + /* TODO */ + break; + } + pdev_network->Configuration.DSConfig = cpu_to_le32( + pregistrypriv->channel); + if (cur_network->network.InfrastructureMode == Ndis802_11IBSS) + pdev_network->Configuration.ATIMWindow = cpu_to_le32(3); + pdev_network->InfrastructureMode = cpu_to_le32( + cur_network->network.InfrastructureMode); + /* 1. Supported rates + * 2. IE + */ + sz = r8712_generate_ie(pregistrypriv, adapter); + pdev_network->IELength = sz; + pdev_network->Length = r8712_get_ndis_wlan_bssid_ex_sz( + (struct ndis_wlan_bssid_ex *)pdev_network); +} + +/*the fucntion is at passive_level*/ +void r8712_joinbss_reset(struct _adapter *padapter) +{ + int i; + struct mlme_priv *pmlmepriv = &padapter->mlmepriv; + struct ht_priv *phtpriv = &pmlmepriv->htpriv; + + /* todo: if you want to do something io/reg/hw setting before join_bss, + * please add code here */ + phtpriv->ampdu_enable = false;/*reset to disabled*/ + for (i = 0; i < 16; i++) + phtpriv->baddbareq_issued[i] = false;/*reset it*/ + if (phtpriv->ht_option) { + /* validate usb rx aggregation */ + r8712_write8(padapter, 0x102500D9, 48);/*TH = 48 pages, 6k*/ + } else { + /* invalidate usb rx aggregation */ + /* TH=1 => means that invalidate usb rx aggregation */ + r8712_write8(padapter, 0x102500D9, 1); + } +} + +/*the function is >= passive_level*/ +unsigned int r8712_restructure_ht_ie(struct _adapter *padapter, u8 *in_ie, + u8 *out_ie, uint in_len, uint *pout_len) +{ + u32 ielen, out_len; + unsigned char *p, *pframe; + struct ieee80211_ht_cap ht_capie; + unsigned char WMM_IE[] = {0x00, 0x50, 0xf2, 0x02, 0x00, 0x01, 0x00}; + struct mlme_priv *pmlmepriv = &padapter->mlmepriv; + struct qos_priv *pqospriv = &pmlmepriv->qospriv; + struct ht_priv *phtpriv = &pmlmepriv->htpriv; + + phtpriv->ht_option = 0; + p = r8712_get_ie(in_ie+12, _HT_CAPABILITY_IE_, &ielen, in_len-12); + if (p && (ielen > 0)) { + if (pqospriv->qos_option == 0) { + out_len = *pout_len; + pframe = r8712_set_ie(out_ie+out_len, + _VENDOR_SPECIFIC_IE_, + _WMM_IE_Length_, + WMM_IE, pout_len); + pqospriv->qos_option = 1; + } + out_len = *pout_len; + memset(&ht_capie, 0, sizeof(struct ieee80211_ht_cap)); + ht_capie.cap_info = IEEE80211_HT_CAP_SUP_WIDTH | + IEEE80211_HT_CAP_SGI_20 | + IEEE80211_HT_CAP_SGI_40 | + IEEE80211_HT_CAP_TX_STBC | + IEEE80211_HT_CAP_MAX_AMSDU | + IEEE80211_HT_CAP_DSSSCCK40; + ht_capie.ampdu_params_info = (IEEE80211_HT_CAP_AMPDU_FACTOR & + 0x03) | (IEEE80211_HT_CAP_AMPDU_DENSITY & 0x00); + pframe = r8712_set_ie(out_ie+out_len, _HT_CAPABILITY_IE_, + sizeof(struct ieee80211_ht_cap), + (unsigned char *)&ht_capie, pout_len); + phtpriv->ht_option = 1; + } + return phtpriv->ht_option; +} + +/* the fucntion is > passive_level (in critical_section) */ +static void update_ht_cap(struct _adapter *padapter, u8 *pie, uint ie_len) +{ + u8 *p, max_ampdu_sz; + int i, len; + struct sta_info *bmc_sta, *psta; + struct ieee80211_ht_cap *pht_capie; + struct ieee80211_ht_addt_info *pht_addtinfo; + struct recv_reorder_ctrl *preorder_ctrl; + struct mlme_priv *pmlmepriv = &padapter->mlmepriv; + struct ht_priv *phtpriv = &pmlmepriv->htpriv; + struct registry_priv *pregistrypriv = &padapter->registrypriv; + struct wlan_network *pcur_network = &(pmlmepriv->cur_network); + + if (!phtpriv->ht_option) + return; + /* maybe needs check if ap supports rx ampdu. */ + if ((phtpriv->ampdu_enable == false) && + (pregistrypriv->ampdu_enable == 1)) + phtpriv->ampdu_enable = true; + /*check Max Rx A-MPDU Size*/ + len = 0; + p = r8712_get_ie(pie + sizeof(struct NDIS_802_11_FIXED_IEs), + _HT_CAPABILITY_IE_, + &len, ie_len - + sizeof(struct NDIS_802_11_FIXED_IEs)); + if (p && len > 0) { + pht_capie = (struct ieee80211_ht_cap *)(p+2); + max_ampdu_sz = (pht_capie->ampdu_params_info & + IEEE80211_HT_CAP_AMPDU_FACTOR); + /* max_ampdu_sz (kbytes); */ + max_ampdu_sz = 1 << (max_ampdu_sz+3); + phtpriv->rx_ampdu_maxlen = max_ampdu_sz; + } + /* for A-MPDU Rx reordering buffer control for bmc_sta & sta_info + * if A-MPDU Rx is enabled, reseting rx_ordering_ctrl + * wstart_b(indicate_seq) to default value=0xffff + * todo: check if AP can send A-MPDU packets + */ + bmc_sta = r8712_get_bcmc_stainfo(padapter); + if (bmc_sta) { + for (i = 0; i < 16; i++) { + preorder_ctrl = &bmc_sta->recvreorder_ctrl[i]; + preorder_ctrl->indicate_seq = 0xffff; + preorder_ctrl->wend_b = 0xffff; + } + } + psta = r8712_get_stainfo(&padapter->stapriv, + pcur_network->network.MacAddress); + if (psta) { + for (i = 0; i < 16 ; i++) { + preorder_ctrl = &psta->recvreorder_ctrl[i]; + preorder_ctrl->indicate_seq = 0xffff; + preorder_ctrl->wend_b = 0xffff; + } + } + len = 0; + p = r8712_get_ie(pie + sizeof(struct NDIS_802_11_FIXED_IEs), + _HT_ADD_INFO_IE_, &len, + ie_len-sizeof(struct NDIS_802_11_FIXED_IEs)); + if (p && len > 0) + pht_addtinfo = (struct ieee80211_ht_addt_info *)(p + 2); +} + +void r8712_issue_addbareq_cmd(struct _adapter *padapter, int priority) +{ + struct mlme_priv *pmlmepriv = &padapter->mlmepriv; + struct ht_priv *phtpriv = &pmlmepriv->htpriv; + + if ((phtpriv->ht_option == 1) && (phtpriv->ampdu_enable == true)) { + if (phtpriv->baddbareq_issued[priority] == false) { + r8712_addbareq_cmd(padapter, (u8)priority); + phtpriv->baddbareq_issued[priority] = true; + } + } +} + +/*the fucntion is >= passive_level*/ +unsigned int r8712_add_ht_addt_info(struct _adapter *padapter, + u8 *in_ie, u8 *out_ie, + uint in_len, uint *pout_len) +{ + u32 ielen, out_len = 0; + unsigned char *p, *pframe; + struct ieee80211_ht_addt_info ht_addt_info; + struct mlme_priv *pmlmepriv = &padapter->mlmepriv; + struct ht_priv *phtpriv = &pmlmepriv->htpriv; + struct registry_priv *pregistrypriv = &padapter->registrypriv; + out_len = *pout_len; + + if (pregistrypriv->ht_enable == 1) { + p = r8712_get_ie(in_ie+12, _HT_ADD_INFO_IE_, + &ielen, in_len - 12); + if (p && (ielen > 0)) { + ; /* dummy branch */ + } else { + if (p == NULL) { + int sz = sizeof(struct ieee80211_ht_addt_info); + memset(&ht_addt_info, 0, sz); + /*need to add the HT additional IEs*/ + ht_addt_info.control_chan = + pregistrypriv->channel; + pframe = r8712_set_ie(out_ie + out_len, + _HT_ADD_INFO_IE_, + sz, + (unsigned char *)&ht_addt_info, + pout_len); + } + } + } + return phtpriv->ht_option; +}