diff options
Diffstat (limited to 'drivers/staging/rtl8712/rtl871x_mlme.c')
-rw-r--r-- | drivers/staging/rtl8712/rtl871x_mlme.c | 1842 |
1 files changed, 1842 insertions, 0 deletions
diff --git a/drivers/staging/rtl8712/rtl871x_mlme.c b/drivers/staging/rtl8712/rtl871x_mlme.c new file mode 100644 index 000000000000..fb3508a012c9 --- /dev/null +++ b/drivers/staging/rtl8712/rtl871x_mlme.c @@ -0,0 +1,1842 @@ +/****************************************************************************** + * 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 <wlanfae@realtek.com> + * Larry Finger <Larry.Finger@lwfinger.net> + * + ******************************************************************************/ + +#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, "<hidden>", 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; + struct __queue *ppending_recvframe_queue = NULL; + unsigned long irql; + + 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); + precvreorder_ctrl = &psta->recvreorder_ctrl[pAddbareq_pram->tid]; + ppending_recvframe_queue = &precvreorder_ctrl->pending_recvframe_queue; + spin_lock_irqsave(&ppending_recvframe_queue->lock, irql); + r8712_recv_indicatepkts_in_order(adapter, precvreorder_ctrl, true); + spin_unlock_irqrestore(&ppending_recvframe_queue->lock, irql); + /* 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) |