diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2010-10-28 12:13:00 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-10-28 12:13:00 -0700 |
commit | f063a0c0c995d010960efcc1b2ed14b99674f25c (patch) | |
tree | 106e30acd1a58b3cf9f3c15abe1de83f1919e03b /drivers/staging/rtl8712/rtl871x_ioctl_linux.c | |
parent | 3c3762957818dc902222733a8184f23102e24472 (diff) | |
parent | 5af634789c93b97cfb314a102436716be8fbc577 (diff) | |
download | op-kernel-dev-f063a0c0c995d010960efcc1b2ed14b99674f25c.zip op-kernel-dev-f063a0c0c995d010960efcc1b2ed14b99674f25c.tar.gz |
Merge git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging-2.6
* git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging-2.6: (841 commits)
Staging: brcm80211: fix usage of roundup in structures
Staging: bcm: fix up network device reference counting
Staging: keucr: fix up US_ macro change
staging: brcm80211: brcmfmac: Removed codeversion from firmware filenames.
staging: brcm80211: Remove unnecessary header files.
staging: brcm80211: Remove unnecessary includes from bcmutils.c
staging: brcm80211: Removed unnecessary pktsetprio() function.
Staging: brcm80211: remove typedefs.h
Staging: brcm80211: remove uintptr typedef usage
Staging: hv: remove struct vmbus_channel_interface
Staging: hv: remove Open from struct vmbus_channel_interface
Staging: hv: storvsc: call vmbus_open directly
Staging: hv: netvsc: call vmbus_open directly
Staging: hv: channel: export vmbus_open to modules
Staging: hv: remove Close from struct vmbus_channel_interface
Staging: hv: netvsc: call vmbus_close directly
Staging: hv: storvsc: call vmbus_close directly
Staging: hv: channel: export vmbus_close to modules
Staging: hv: remove SendPacket from struct vmbus_channel_interface
Staging: hv: storvsc: call vmbus_sendpacket directly
...
Fix up conflicts in
drivers/staging/cx25821/cx25821-audio-upstream.c
drivers/staging/cx25821/cx25821-audio.h
due to warring whitespace cleanups (neither of which were all that great)
Diffstat (limited to 'drivers/staging/rtl8712/rtl871x_ioctl_linux.c')
-rw-r--r-- | drivers/staging/rtl8712/rtl871x_ioctl_linux.c | 2246 |
1 files changed, 2246 insertions, 0 deletions
diff --git a/drivers/staging/rtl8712/rtl871x_ioctl_linux.c b/drivers/staging/rtl8712/rtl871x_ioctl_linux.c new file mode 100644 index 0000000..685a7b1 --- /dev/null +++ b/drivers/staging/rtl8712/rtl871x_ioctl_linux.c @@ -0,0 +1,2246 @@ +/****************************************************************************** + * rtl871x_ioctl_linux.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_IOCTL_LINUX_C_ + +#include "osdep_service.h" +#include "drv_types.h" +#include "wlan_bssdef.h" +#include "rtl871x_debug.h" +#include "wifi.h" +#include "rtl871x_mlme.h" +#include "rtl871x_ioctl.h" +#include "rtl871x_ioctl_set.h" +#include "rtl871x_mp_ioctl.h" +#include "mlme_osdep.h" + +#define RTL_IOCTL_WPA_SUPPLICANT (SIOCIWFIRSTPRIV + 30) + +#define SCAN_ITEM_SIZE 768 +#define MAX_CUSTOM_LEN 64 +#define RATE_COUNT 4 + + +static const u32 rtl8180_rates[] = {1000000, 2000000, 5500000, 11000000, + 6000000, 9000000, 12000000, 18000000, + 24000000, 36000000, 48000000, 54000000}; + +static const long ieee80211_wlan_frequencies[] = { + 2412, 2417, 2422, 2427, + 2432, 2437, 2442, 2447, + 2452, 2457, 2462, 2467, + 2472, 2484 +}; + +static const char * const iw_operation_mode[] = { + "Auto", "Ad-Hoc", "Managed", "Master", "Repeater", "Secondary", + "Monitor" +}; + +/** + * hwaddr_aton - Convert ASCII string to MAC address + * @txt: MAC address as a string (e.g., "00:11:22:33:44:55") + * @addr: Buffer for the MAC address (ETH_ALEN = 6 bytes) + * Returns: 0 on success, -1 on failure (e.g., string not a MAC address) + */ +static int hwaddr_aton_i(const char *txt, u8 *addr) +{ + int i; + + for (i = 0; i < 6; i++) { + int a, b; + + a = hex_to_bin(*txt++); + if (a < 0) + return -1; + b = hex_to_bin(*txt++); + if (b < 0) + return -1; + *addr++ = (a << 4) | b; + if (i < 5 && *txt++ != ':') + return -1; + } + return 0; +} + +void r8712_indicate_wx_assoc_event(struct _adapter *padapter) +{ + union iwreq_data wrqu; + struct mlme_priv *pmlmepriv = &padapter->mlmepriv; + + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + memcpy(wrqu.ap_addr.sa_data, pmlmepriv->cur_network.network.MacAddress, + ETH_ALEN); + wireless_send_event(padapter->pnetdev, SIOCGIWAP, &wrqu, NULL); +} + +void r8712_indicate_wx_disassoc_event(struct _adapter *padapter) +{ + union iwreq_data wrqu; + + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + memset(wrqu.ap_addr.sa_data, 0, ETH_ALEN); + wireless_send_event(padapter->pnetdev, SIOCGIWAP, &wrqu, NULL); +} + +static inline void handle_pairwise_key(struct sta_info *psta, + struct ieee_param *param, + struct _adapter *padapter) +{ + /* pairwise key */ + memcpy(psta->x_UncstKey.skey, param->u.crypt.key, + (param->u.crypt. key_len > 16 ? 16 : param->u.crypt.key_len)); + if (strcmp(param->u.crypt.alg, "TKIP") == 0) { /* set mic key */ + memcpy(psta->tkiptxmickey. skey, &(param->u.crypt. + key[16]), 8); + memcpy(psta->tkiprxmickey. skey, &(param->u.crypt. + key[24]), 8); + padapter->securitypriv. busetkipkey = false; + _set_timer(&padapter->securitypriv.tkip_timer, 50); + } + r8712_setstakey_cmd(padapter, (unsigned char *)psta, true); +} + +static inline void handle_group_key(struct ieee_param *param, + struct _adapter *padapter) +{ + if (0 < param->u.crypt.idx && + param->u.crypt.idx < 3) { + /* group key idx is 1 or 2 */ + memcpy(padapter->securitypriv.XGrpKey[param->u.crypt. + idx-1].skey, param->u.crypt.key, (param->u.crypt.key_len + > 16 ? 16 : param->u.crypt.key_len)); + memcpy(padapter->securitypriv.XGrptxmickey[param-> + u.crypt.idx-1].skey, &(param->u.crypt.key[16]), 8); + memcpy(padapter->securitypriv. XGrprxmickey[param-> + u.crypt.idx-1].skey, &(param->u.crypt.key[24]), 8); + padapter->securitypriv.binstallGrpkey = true; + r8712_set_key(padapter, &padapter->securitypriv, + param->u.crypt.idx); + if (padapter->registrypriv.power_mgnt > PS_MODE_ACTIVE) { + if (padapter->registrypriv.power_mgnt != padapter-> + pwrctrlpriv.pwr_mode) + _set_timer(&(padapter->mlmepriv.dhcp_timer), + 60000); + } + } +} + +static inline char *translate_scan(struct _adapter *padapter, + struct iw_request_info *info, + struct wlan_network *pnetwork, + char *start, char *stop) +{ + struct iw_event iwe; + struct ieee80211_ht_cap *pht_capie; + char *current_val; + u8 *buf = (u8 *)_malloc(pnetwork->network.IELength * 2); + u8 *wpa_ie = (u8 *)_malloc(255); + u8 *rsn_ie = (u8 *)_malloc(255); + u8 *wps_ie = (u8 *)_malloc(MAX_WPS_IE_LEN); + s8 *p; + u32 i = 0, ht_ielen = 0; + u16 cap, ht_cap = false, mcs_rate; + u8 rssi, bw_40MHz = 0, short_GI = 0; + + if ((pnetwork->network.Configuration.DSConfig < 1) || + (pnetwork->network.Configuration.DSConfig > 14)) { + if (pnetwork->network.Configuration.DSConfig < 1) + pnetwork->network.Configuration.DSConfig = 1; + else + pnetwork->network.Configuration.DSConfig = 14; + } + /* AP MAC address */ + iwe.cmd = SIOCGIWAP; + iwe.u.ap_addr.sa_family = ARPHRD_ETHER; + memcpy(iwe.u.ap_addr.sa_data, pnetwork->network.MacAddress, ETH_ALEN); + start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_ADDR_LEN); + /* Add the ESSID */ + iwe.cmd = SIOCGIWESSID; + iwe.u.data.flags = 1; + iwe.u.data.length = (u16)min((u16)pnetwork->network.Ssid.SsidLength, + (u16)32); + start = iwe_stream_add_point(info, start, stop, &iwe, + pnetwork->network.Ssid.Ssid); + /* parsing HT_CAP_IE */ + p = r8712_get_ie(&pnetwork->network.IEs[12], _HT_CAPABILITY_IE_, + &ht_ielen, pnetwork->network.IELength - 12); + if (p && ht_ielen > 0) { + ht_cap = true; + pht_capie = (struct ieee80211_ht_cap *)(p + 2); + memcpy(&mcs_rate , pht_capie->supp_mcs_set, 2); + bw_40MHz = (pht_capie->cap_info&IEEE80211_HT_CAP_SUP_WIDTH) + ? 1 : 0; + short_GI = (pht_capie->cap_info&(IEEE80211_HT_CAP_SGI_20 | + IEEE80211_HT_CAP_SGI_40)) ? 1 : 0; + } + /* Add the protocol name */ + iwe.cmd = SIOCGIWNAME; + if ((r8712_is_cckratesonly_included((u8 *)&pnetwork->network. + SupportedRates)) == true) { + if (ht_cap == true) + snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11bn"); + else + snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11b"); + } else if ((r8712_is_cckrates_included((u8 *)&pnetwork->network. + SupportedRates)) == true) { + if (ht_cap == true) + snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11bgn"); + else + snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11bg"); + } else { + if (ht_cap == true) + snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11gn"); + else + snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11g"); + } + start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_CHAR_LEN); + /* Add mode */ + iwe.cmd = SIOCGIWMODE; + memcpy((u8 *)&cap, r8712_get_capability_from_ie(pnetwork->network.IEs), + 2); + cap = le16_to_cpu(cap); + if (cap & (WLAN_CAPABILITY_IBSS|WLAN_CAPABILITY_BSS)) { + if (cap & WLAN_CAPABILITY_BSS) + iwe.u.mode = (u32)IW_MODE_MASTER; + else + iwe.u.mode = (u32)IW_MODE_ADHOC; + start = iwe_stream_add_event(info, start, stop, &iwe, + IW_EV_UINT_LEN); + } + /* Add frequency/channel */ + iwe.cmd = SIOCGIWFREQ; + { + /* check legel index */ + u8 dsconfig = pnetwork->network.Configuration.DSConfig; + if (dsconfig >= 1 && dsconfig <= sizeof( + ieee80211_wlan_frequencies) / sizeof(long)) + iwe.u.freq.m = (s32)(ieee80211_wlan_frequencies[ + pnetwork->network.Configuration. + DSConfig - 1] * 100000); + else + iwe.u.freq.m = 0; + } + iwe.u.freq.e = (s16)1; + iwe.u.freq.i = (u8)pnetwork->network.Configuration.DSConfig; + start = iwe_stream_add_event(info, start, stop, &iwe, + IW_EV_FREQ_LEN); + /* Add encryption capability */ + iwe.cmd = SIOCGIWENCODE; + if (cap & WLAN_CAPABILITY_PRIVACY) + iwe.u.data.flags = (u16)(IW_ENCODE_ENABLED | + IW_ENCODE_NOKEY); + else + iwe.u.data.flags = (u16)(IW_ENCODE_DISABLED); + iwe.u.data.length = (u16)0; + start = iwe_stream_add_point(info, start, stop, &iwe, + pnetwork->network.Ssid.Ssid); + /*Add basic and extended rates */ + current_val = start + iwe_stream_lcp_len(info); + iwe.cmd = SIOCGIWRATE; + iwe.u.bitrate.fixed = 0; + iwe.u.bitrate.disabled = 0; + iwe.u.bitrate.value = 0; + i = 0; + while (pnetwork->network.SupportedRates[i] != 0) { + /* Bit rate given in 500 kb/s units */ + iwe.u.bitrate.value = (pnetwork->network.SupportedRates[i++] & + 0x7F) * 500000; + current_val = iwe_stream_add_value(info, start, current_val, + stop, &iwe, IW_EV_PARAM_LEN); + } + /* Check if we added any event */ + if ((current_val - start) > iwe_stream_lcp_len(info)) + start = current_val; + /* parsing WPA/WPA2 IE */ + { + u16 wpa_len = 0, rsn_len = 0; + u8 *p; + sint out_len = 0; + out_len = r8712_get_sec_ie(pnetwork->network.IEs, + pnetwork->network. + IELength, rsn_ie, &rsn_len, + wpa_ie, &wpa_len); + if (wpa_len > 0) { + p = buf; + memset(buf, 0, MAX_WPA_IE_LEN); + p += snprintf(p, 7, "wpa_ie="); + for (i = 0; i < wpa_len; i++) + p += snprintf(p, 2, "%02x", wpa_ie[i]); + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = IWEVCUSTOM; + iwe.u.data.length = (u16)strlen(buf); + start = iwe_stream_add_point(info, start, stop, + &iwe, buf); + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = IWEVGENIE; + iwe.u.data.length = (u16)wpa_len; + start = iwe_stream_add_point(info, start, stop, + &iwe, wpa_ie); + } + if (rsn_len > 0) { + p = buf; + memset(buf, 0, MAX_WPA_IE_LEN); + p += snprintf(p, 7, "rsn_ie="); + for (i = 0; i < rsn_len; i++) + p += snprintf(p, 2, "%02x", rsn_ie[i]); + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = IWEVCUSTOM; + iwe.u.data.length = strlen(buf); + start = iwe_stream_add_point(info, start, stop, + &iwe, buf); + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = IWEVGENIE; + iwe.u.data.length = rsn_len; + start = iwe_stream_add_point(info, start, stop, &iwe, + rsn_ie); + } + } + + { /* parsing WPS IE */ + uint wps_ielen; + + if (r8712_get_wps_ie(pnetwork->network.IEs, + pnetwork->network.IELength, + wps_ie, &wps_ielen) == true) { + if (wps_ielen > 2) { + iwe.cmd = IWEVGENIE; + iwe.u.data.length = (u16)wps_ielen; + start = iwe_stream_add_point(info, start, stop, + &iwe, wps_ie); + } + } + } + /* Add quality statistics */ + iwe.cmd = IWEVQUAL; + rssi = r8712_signal_scale_mapping(pnetwork->network.Rssi); + /* we only update signal_level (signal strength) that is rssi. */ + iwe.u.qual.updated = (u8)(IW_QUAL_QUAL_INVALID | IW_QUAL_LEVEL_UPDATED | + IW_QUAL_NOISE_INVALID); + iwe.u.qual.level = rssi; /* signal strength */ + iwe.u.qual.qual = 0; /* signal quality */ + iwe.u.qual.noise = 0; /* noise level */ + start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_QUAL_LEN); + /* how to translate rssi to ?% */ + kfree(buf); + kfree(wpa_ie); + kfree(rsn_ie); + kfree(wps_ie); + return start; +} + +static int wpa_set_auth_algs(struct net_device *dev, u32 value) +{ + struct _adapter *padapter = (struct _adapter *) _netdev_priv(dev); + int ret = 0; + + if ((value & AUTH_ALG_SHARED_KEY) && (value & AUTH_ALG_OPEN_SYSTEM)) { + padapter->securitypriv.ndisencryptstatus = + Ndis802_11Encryption1Enabled; + padapter->securitypriv.ndisauthtype = + Ndis802_11AuthModeAutoSwitch; + padapter->securitypriv.AuthAlgrthm = 3; + } else if (value & AUTH_ALG_SHARED_KEY) { + padapter->securitypriv.ndisencryptstatus = + Ndis802_11Encryption1Enabled; + padapter->securitypriv.ndisauthtype = Ndis802_11AuthModeShared; + padapter->securitypriv.AuthAlgrthm = 1; + } else if (value & AUTH_ALG_OPEN_SYSTEM) { + if (padapter->securitypriv.ndisauthtype < + Ndis802_11AuthModeWPAPSK) { + padapter->securitypriv.ndisauthtype = + Ndis802_11AuthModeOpen; + padapter->securitypriv.AuthAlgrthm = 0; + } + } else + ret = -EINVAL; + return ret; +} + +static int wpa_set_encryption(struct net_device *dev, struct ieee_param *param, + u32 param_len) +{ + int ret = 0; + u32 wep_key_idx, wep_key_len = 0; + struct NDIS_802_11_WEP *pwep = NULL; + struct _adapter *padapter = (struct _adapter *)_netdev_priv(dev); + struct mlme_priv *pmlmepriv = &padapter->mlmepriv; + struct security_priv *psecuritypriv = &padapter->securitypriv; + + param->u.crypt.err = 0; + param->u.crypt.alg[IEEE_CRYPT_ALG_NAME_LEN - 1] = '\0'; + if (param_len != (u32)((u8 *) param->u.crypt.key - (u8 *)param) + + param->u.crypt.key_len) + return -EINVAL; + if (param->sta_addr[0] == 0xff && param->sta_addr[1] == 0xff && + param->sta_addr[2] == 0xff && param->sta_addr[3] == 0xff && + param->sta_addr[4] == 0xff && param->sta_addr[5] == 0xff) { + if (param->u.crypt.idx >= WEP_KEYS) { + /* for large key indices, set the default (0) */ + param->u.crypt.idx = 0; + } + } else + return -EINVAL; + if (strcmp(param->u.crypt.alg, "WEP") == 0) { + printk(KERN_INFO "r8712u: wpa_set_encryption, crypt.alg =" + " WEP\n"); + padapter->securitypriv.ndisencryptstatus = + Ndis802_11Encryption1Enabled; + padapter->securitypriv.PrivacyAlgrthm = _WEP40_; + padapter->securitypriv.XGrpPrivacy = _WEP40_; + wep_key_idx = param->u.crypt.idx; + wep_key_len = param->u.crypt.key_len; + if (wep_key_idx >= WEP_KEYS) + wep_key_idx = 0; + if (wep_key_len > 0) { + wep_key_len = wep_key_len <= 5 ? 5 : 13; + pwep = (struct NDIS_802_11_WEP *)_malloc((u32) + (wep_key_len + + FIELD_OFFSET(struct NDIS_802_11_WEP, + KeyMaterial))); + if (pwep == NULL) + return -ENOMEM; + memset(pwep, 0, sizeof(struct NDIS_802_11_WEP)); + pwep->KeyLength = wep_key_len; + pwep->Length = wep_key_len + + FIELD_OFFSET(struct NDIS_802_11_WEP, + KeyMaterial); + if (wep_key_len == 13) { + padapter->securitypriv.PrivacyAlgrthm = + _WEP104_; + padapter->securitypriv.XGrpPrivacy = + _WEP104_; + } + } else + return -EINVAL; + pwep->KeyIndex = wep_key_idx; + pwep->KeyIndex |= 0x80000000; + memcpy(pwep->KeyMaterial, param->u.crypt.key, pwep->KeyLength); + if (param->u.crypt.set_tx) { + if (r8712_set_802_11_add_wep(padapter, pwep) == + (u8)_FAIL) + ret = -EOPNOTSUPP; + } else { + /* don't update "psecuritypriv->PrivacyAlgrthm" and + * "psecuritypriv->PrivacyKeyIndex=keyid", but can + * r8712_set_key to fw/cam + */ + if (wep_key_idx >= WEP_KEYS) { + ret = -EOPNOTSUPP; + goto exit; + } + memcpy(&(psecuritypriv->DefKey[wep_key_idx]. + skey[0]), pwep->KeyMaterial, + pwep->KeyLength); + psecuritypriv->DefKeylen[wep_key_idx] = + pwep->KeyLength; + r8712_set_key(padapter, psecuritypriv, wep_key_idx); + } + goto exit; + } + if (padapter->securitypriv.AuthAlgrthm == 2) { /* 802_1x */ + struct sta_info *psta, *pbcmc_sta; + struct sta_priv *pstapriv = &padapter->stapriv; + + if (check_fwstate(pmlmepriv, WIFI_STATION_STATE | + WIFI_MP_STATE) == true) { /* sta mode */ + psta = r8712_get_stainfo(pstapriv, + get_bssid(pmlmepriv)); + if (psta) { + psta->ieee8021x_blocked = false; + if ((padapter->securitypriv.ndisencryptstatus == + Ndis802_11Encryption2Enabled) || + (padapter->securitypriv.ndisencryptstatus == + Ndis802_11Encryption3Enabled)) + psta->XPrivacy = padapter-> + securitypriv.PrivacyAlgrthm; + if (param->u.crypt.set_tx == 1) + handle_pairwise_key(psta, param, + padapter); + else /* group key */ + handle_group_key(param, padapter); + } + pbcmc_sta = r8712_get_bcmc_stainfo(padapter); + if (pbcmc_sta) { + pbcmc_sta->ieee8021x_blocked = false; + if ((padapter->securitypriv.ndisencryptstatus == + Ndis802_11Encryption2Enabled) || + (padapter->securitypriv.ndisencryptstatus == + Ndis802_11Encryption3Enabled)) + pbcmc_sta->XPrivacy = + padapter->securitypriv. + PrivacyAlgrthm; + } + } + } +exit: + kfree((u8 *)pwep); + return ret; +} + +static int r871x_set_wpa_ie(struct _adapter *padapter, char *pie, + unsigned short ielen) +{ + u8 *buf = NULL, *pos = NULL; + int group_cipher = 0, pairwise_cipher = 0; + int ret = 0; + + if ((ielen > MAX_WPA_IE_LEN) || (pie == NULL)) + return -EINVAL; + if (ielen) { + buf = _malloc(ielen); + if (buf == NULL) + return -ENOMEM; + memcpy(buf, pie , ielen); + pos = buf; + if (ielen < RSN_HEADER_LEN) { + ret = -1; + goto exit; + } + if (r8712_parse_wpa_ie(buf, ielen, &group_cipher, + &pairwise_cipher) == _SUCCESS) { + padapter->securitypriv.AuthAlgrthm = 2; + padapter->securitypriv.ndisauthtype = + Ndis802_11AuthModeWPAPSK; + } + if (r8712_parse_wpa2_ie(buf, ielen, &group_cipher, + &pairwise_cipher) == _SUCCESS) { + padapter->securitypriv.AuthAlgrthm = 2; + padapter->securitypriv.ndisauthtype = + Ndis802_11AuthModeWPA2PSK; + } + switch (group_cipher) { + case WPA_CIPHER_NONE: + padapter->securitypriv.XGrpPrivacy = + _NO_PRIVACY_; + padapter->securitypriv.ndisencryptstatus = + Ndis802_11EncryptionDisabled; + break; + case WPA_CIPHER_WEP40: + padapter->securitypriv.XGrpPrivacy = _WEP40_; + padapter->securitypriv.ndisencryptstatus = + Ndis802_11Encryption1Enabled; + break; + case WPA_CIPHER_TKIP: + padapter->securitypriv.XGrpPrivacy = _TKIP_; + padapter->securitypriv.ndisencryptstatus = + Ndis802_11Encryption2Enabled; + break; + case WPA_CIPHER_CCMP: + padapter->securitypriv.XGrpPrivacy = _AES_; + padapter->securitypriv.ndisencryptstatus = + Ndis802_11Encryption3Enabled; + break; + case WPA_CIPHER_WEP104: + padapter->securitypriv.XGrpPrivacy = _WEP104_; + padapter->securitypriv.ndisencryptstatus = + Ndis802_11Encryption1Enabled; + break; + } + switch (pairwise_cipher) { + case WPA_CIPHER_NONE: + padapter->securitypriv.PrivacyAlgrthm = + _NO_PRIVACY_; + padapter->securitypriv.ndisencryptstatus = + Ndis802_11EncryptionDisabled; + break; + case WPA_CIPHER_WEP40: + padapter->securitypriv.PrivacyAlgrthm = _WEP40_; + padapter->securitypriv.ndisencryptstatus = + Ndis802_11Encryption1Enabled; + break; + case WPA_CIPHER_TKIP: + padapter->securitypriv.PrivacyAlgrthm = _TKIP_; + padapter->securitypriv.ndisencryptstatus = + Ndis802_11Encryption2Enabled; + break; + case WPA_CIPHER_CCMP: + padapter->securitypriv.PrivacyAlgrthm = _AES_; + padapter->securitypriv.ndisencryptstatus = + Ndis802_11Encryption3Enabled; + break; + case WPA_CIPHER_WEP104: + padapter->securitypriv.PrivacyAlgrthm = _WEP104_; + padapter->securitypriv.ndisencryptstatus = + Ndis802_11Encryption1Enabled; + break; + } + padapter->securitypriv.wps_phase = false; + {/* set wps_ie */ + u16 cnt = 0; + u8 eid, wps_oui[4] = {0x0, 0x50, 0xf2, 0x04}; + + while (cnt < ielen) { + eid = buf[cnt]; + + if ((eid == _VENDOR_SPECIFIC_IE_) && + (!memcmp(&buf[cnt+2], wps_oui, 4))) { + printk(KERN_INFO "r8712u: " + "SET WPS_IE\n"); + padapter->securitypriv.wps_ie_len = + ((buf[cnt+1] + 2) < + (MAX_WPA_IE_LEN << 2)) ? + (buf[cnt + 1] + 2) : + (MAX_WPA_IE_LEN << 2); + memcpy(padapter->securitypriv.wps_ie, + &buf[cnt], + padapter->securitypriv.wps_ie_len); + padapter->securitypriv.wps_phase = + true; + printk(KERN_INFO "r8712u: SET WPS_IE," + " wps_phase==true\n"); + cnt += buf[cnt+1]+2; + break; + } else + cnt += buf[cnt + 1] + 2; + } + } + } +exit: + kfree(buf); + return ret; +} + +static int r8711_wx_get_name(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct _adapter *padapter = (struct _adapter *)_netdev_priv(dev); + u32 ht_ielen = 0; + char *p; + u8 ht_cap = false; + struct mlme_priv *pmlmepriv = &(padapter->mlmepriv); + struct ndis_wlan_bssid_ex *pcur_bss = &pmlmepriv->cur_network.network; + NDIS_802_11_RATES_EX *prates = NULL; + + if (check_fwstate(pmlmepriv, _FW_LINKED|WIFI_ADHOC_MASTER_STATE) == + true) { + /* parsing HT_CAP_IE */ + p = r8712_get_ie(&pcur_bss->IEs[12], _HT_CAPABILITY_IE_, + &ht_ielen, pcur_bss->IELength - 12); + if (p && ht_ielen > 0) + ht_cap = true; + prates = &pcur_bss->SupportedRates; + if (r8712_is_cckratesonly_included((u8 *)prates) == true) { + if (ht_cap == true) + snprintf(wrqu->name, IFNAMSIZ, + "IEEE 802.11bn"); + else + snprintf(wrqu->name, IFNAMSIZ, + "IEEE 802.11b"); + } else if ((r8712_is_cckrates_included((u8 *)prates)) == true) { + if (ht_cap == true) + snprintf(wrqu->name, IFNAMSIZ, + "IEEE 802.11bgn"); + else + snprintf(wrqu->name, IFNAMSIZ, + "IEEE 802.11bg"); + } else { + if (ht_cap == true) + snprintf(wrqu->name, IFNAMSIZ, + "IEEE 802.11gn"); + else + snprintf(wrqu->name, IFNAMSIZ, + "IEEE 802.11g"); + } + } else + snprintf(wrqu->name, IFNAMSIZ, "unassociated"); + return 0; +} + +static const long frequency_list[] = { + 2412, 2417, 2422, 2427, 2432, 2437, 2442, 2447, 2452, 2457, 2462, + 2467, 2472, 2484, 4915, 4920, 4925, 4935, 4940, 4945, 4960, 4980, + 5035, 5040, 5045, 5055, 5060, 5080, 5170, 5180, 5190, 5200, 5210, + 5220, 5230, 5240, 5260, 5280, 5300, 5320, 5500, 5520, 5540, 5560, + 5580, 5600, 5620, 5640, 5660, 5680, 5700, 5745, 5765, 5785, 5805, + 5825 +}; + +static int r8711_wx_set_freq(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct _adapter *padapter = (struct _adapter *)_netdev_priv(dev); + struct iw_freq *fwrq = &wrqu->freq; + int rc = 0; + +/* If setting by frequency, convert to a channel */ + if ((fwrq->e == 1) && + (fwrq->m >= (int) 2.412e8) && + (fwrq->m <= (int) 2.487e8)) { + int f = fwrq->m / 100000; + int c = 0; + while ((c < 14) && (f != frequency_list[c])) + c++; + fwrq->e = 0; + fwrq->m = c + 1; + } + /* Setting by channel number */ + if ((fwrq->m > 14) || (fwrq->e > 0)) + rc = -EOPNOTSUPP; + else { + int channel = fwrq->m; + if ((channel < 1) || (channel > 14)) + rc = -EINVAL; + else { + /* Yes ! We can set it !!! */ + padapter->registrypriv.channel = channel; + } + } + return rc; +} + +static int r8711_wx_get_freq(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct _adapter *padapter = (struct _adapter *)_netdev_priv(dev); + struct mlme_priv *pmlmepriv = &padapter->mlmepriv; + struct ndis_wlan_bssid_ex *pcur_bss = &pmlmepriv->cur_network.network; + + if (check_fwstate(pmlmepriv, _FW_LINKED) == true) { + wrqu->freq.m = ieee80211_wlan_frequencies[ + pcur_bss->Configuration.DSConfig-1] * 100000; + wrqu->freq.e = 1; + wrqu->freq.i = pcur_bss->Configuration.DSConfig; + } else + return -1; + return 0; +} + +static int r8711_wx_set_mode(struct net_device *dev, + struct iw_request_info *a, + union iwreq_data *wrqu, char *b) +{ + struct _adapter *padapter = (struct _adapter *)_netdev_priv(dev); + enum NDIS_802_11_NETWORK_INFRASTRUCTURE networkType; + + switch (wrqu->mode) { + case IW_MODE_AUTO: + networkType = Ndis802_11AutoUnknown; + break; + case IW_MODE_ADHOC: + networkType = Ndis802_11IBSS; + break; + case IW_MODE_MASTER: + networkType = Ndis802_11APMode; + break; + case IW_MODE_INFRA: + networkType = Ndis802_11Infrastructure; + break; + default: + return -EINVAL; + } + if (Ndis802_11APMode == networkType) + r8712_setopmode_cmd(padapter, networkType); + else + r8712_setopmode_cmd(padapter, Ndis802_11AutoUnknown); + if (!r8712_set_802_11_infrastructure_mode(padapter, networkType)) + return -1; + return 0; +} + +static int r8711_wx_get_mode(struct net_device *dev, struct iw_request_info *a, + union iwreq_data *wrqu, char *b) +{ + struct _adapter *padapter = (struct _adapter *)_netdev_priv(dev); + struct mlme_priv *pmlmepriv = &padapter->mlmepriv; + + if (check_fwstate(pmlmepriv, WIFI_STATION_STATE) == true) + wrqu->mode = IW_MODE_INFRA; + else if (check_fwstate(pmlmepriv, + WIFI_ADHOC_MASTER_STATE|WIFI_ADHOC_STATE) == true) + wrqu->mode = IW_MODE_ADHOC; + else if (check_fwstate(pmlmepriv, WIFI_AP_STATE) == true) + wrqu->mode = IW_MODE_MASTER; + else + wrqu->mode = IW_MODE_AUTO; + return 0; +} + +static int r871x_wx_set_pmkid(struct net_device *dev, + struct iw_request_info *a, + union iwreq_data *wrqu, char *extra) +{ + struct _adapter *padapter = (struct _adapter *)_netdev_priv(dev); + struct security_priv *psecuritypriv = &padapter->securitypriv; + struct iw_pmksa *pPMK = (struct iw_pmksa *) extra; + u8 strZeroMacAddress[ETH_ALEN] = {0x00}; + u8 strIssueBssid[ETH_ALEN] = {0x00}; + u8 j, blInserted = false; + int intReturn = false; + +/* + There are the BSSID information in the bssid.sa_data array. + If cmd is IW_PMKSA_FLUSH, it means the wpa_suppplicant wants to clear + all the PMKID information. If cmd is IW_PMKSA_ADD, it means the + wpa_supplicant wants to add a PMKID/BSSID to driver. + If cmd is IW_PMKSA_REMOVE, it means the wpa_supplicant wants to + remove a PMKID/BSSID from driver. +*/ + if (pPMK == NULL) + return -EINVAL; + memcpy(strIssueBssid, pPMK->bssid.sa_data, ETH_ALEN); + switch (pPMK->cmd) { + case IW_PMKSA_ADD: + if (!memcmp(strIssueBssid, strZeroMacAddress, ETH_ALEN)) + return intReturn; + else + intReturn = true; + blInserted = false; + /* overwrite PMKID */ + for (j = 0 ; j < NUM_PMKID_CACHE; j++) { + if (!memcmp(psecuritypriv->PMKIDList[j].Bssid, + strIssueBssid, ETH_ALEN)) { + /* BSSID is matched, the same AP => rewrite + * with new PMKID. */ + printk(KERN_INFO "r8712u: r871x_wx_set_pmkid:" + " BSSID exists in the PMKList.\n"); + memcpy(psecuritypriv->PMKIDList[j].PMKID, + pPMK->pmkid, IW_PMKID_LEN); + psecuritypriv->PMKIDList[j].bUsed = true; + psecuritypriv->PMKIDIndex = j + 1; + blInserted = true; + break; + } + } + if (!blInserted) { + /* Find a new entry */ + printk(KERN_INFO "r8712u: r871x_wx_set_pmkid: Use the" + " new entry index = %d for this PMKID.\n", + psecuritypriv->PMKIDIndex); + memcpy(psecuritypriv->PMKIDList[psecuritypriv-> + PMKIDIndex].Bssid, strIssueBssid, ETH_ALEN); + memcpy(psecuritypriv->PMKIDList[psecuritypriv-> + PMKIDIndex].PMKID, pPMK->pmkid, IW_PMKID_LEN); + psecuritypriv->PMKIDList[psecuritypriv->PMKIDIndex]. + bUsed = true; + psecuritypriv->PMKIDIndex++ ; + if (psecuritypriv->PMKIDIndex == NUM_PMKID_CACHE) + psecuritypriv->PMKIDIndex = 0; + } + break; + case IW_PMKSA_REMOVE: + intReturn = true; + for (j = 0; j < NUM_PMKID_CACHE; j++) { + if (!memcmp(psecuritypriv->PMKIDList[j].Bssid, + strIssueBssid, ETH_ALEN)) { + /* BSSID is matched, the same AP => Remove + * this PMKID information and reset it. */ + memset(psecuritypriv->PMKIDList[j].Bssid, + 0x00, ETH_ALEN); + psecuritypriv->PMKIDList[j].bUsed = false; + break; + } + } + break; + case IW_PMKSA_FLUSH: + memset(psecuritypriv->PMKIDList, 0, + sizeof(struct RT_PMKID_LIST) * NUM_PMKID_CACHE); + psecuritypriv->PMKIDIndex = 0; + intReturn = true; + break; + default: + printk(KERN_INFO "r8712u: r871x_wx_set_pmkid: " + "unknown Command\n"); + intReturn = false; + break; + } + return intReturn; +} + +static int r8711_wx_get_sens(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + wrqu->sens.value = 0; + wrqu->sens.fixed = 0; /* no auto select */ + wrqu->sens.disabled = 1; + return 0; +} + +static int r8711_wx_get_range(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct iw_range *range = (struct iw_range *)extra; + u16 val; + int i; + + wrqu->data.length = sizeof(*range); + memset(range, 0, sizeof(*range)); + /* Let's try to keep this struct in the same order as in + * linux/include/wireless.h + */ + + /* TODO: See what values we can set, and remove the ones we can't + * set, or fill them with some default data. + */ + /* ~5 Mb/s real (802.11b) */ + range->throughput = 5 * 1000 * 1000; + /* TODO: 8711 sensitivity ? */ + /* signal level threshold range */ + /* percent values between 0 and 100. */ + range->max_qual.qual = 100; + range->max_qual.level = 100; + range->max_qual.noise = 100; + range->max_qual.updated = 7; /* Updated all three */ + range->avg_qual.qual = 92; /* > 8% missed beacons is 'bad' */ + /* TODO: Find real 'good' to 'bad' threshol value for RSSI */ + range->avg_qual.level = 20 + -98; + range->avg_qual.noise = 0; + range->avg_qual.updated = 7; /* Updated all three */ + range->num_bitrates = RATE_COUNT; + for (i = 0; i < RATE_COUNT && i < IW_MAX_BITRATES; i++) + range->bitrate[i] = rtl8180_rates[i]; + range->min_frag = MIN_FRAG_THRESHOLD; + range->max_frag = MAX_FRAG_THRESHOLD; + range->pm_capa = 0; + range->we_version_compiled = WIRELESS_EXT; + range->we_version_source = 16; + range->num_channels = 14; + for (i = 0, val = 0; i < 14; i++) { + /* Include only legal frequencies for some countries */ + range->freq[val].i = i + 1; + range->freq[val].m = ieee80211_wlan_frequencies[i] * 100000; + range->freq[val].e = 1; + val++; + if (val == IW_MAX_FREQUENCIES) + break; + } + range->num_frequency = val; + range->enc_capa = IW_ENC_CAPA_WPA | + IW_ENC_CAPA_WPA2 | + IW_ENC_CAPA_CIPHER_TKIP | + IW_ENC_CAPA_CIPHER_CCMP; + return 0; +} + +static int r871x_wx_set_priv(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *awrq, + char *extra) +{ + int ret = 0, len = 0; + char *ext; + struct iw_point *dwrq = (struct iw_point *)awrq; + + len = dwrq->length; + ext = _malloc(len); + if (!_malloc(len)) + return -ENOMEM; + if (copy_from_user(ext, dwrq->pointer, len)) { + kfree(ext); + return -EFAULT; + } + kfree(ext); + return ret; +} + +/* set bssid flow + * s1. set_802_11_infrastructure_mode() + * s2. set_802_11_authentication_mode() + * s3. set_802_11_encryption_mode() + * s4. set_802_11_bssid() + */ +static int r8711_wx_set_wap(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *awrq, + char *extra) +{ + int ret = -EINPROGRESS; + struct _adapter *padapter = (struct _adapter *) _netdev_priv(dev); + struct mlme_priv *pmlmepriv = &padapter->mlmepriv; + struct __queue *queue = &pmlmepriv->scanned_queue; + struct sockaddr *temp = (struct sockaddr *)awrq; + unsigned long irqL; + struct list_head *phead; + u8 *dst_bssid; + struct wlan_network *pnetwork = NULL; + enum NDIS_802_11_AUTHENTICATION_MODE authmode; + + if (padapter->bup == false) + return -1; + if (check_fwstate(pmlmepriv, _FW_UNDER_SURVEY) == true) + return -1; + if (check_fwstate(pmlmepriv, _FW_UNDER_LINKING) == true) + return ret; + if (temp->sa_family != ARPHRD_ETHER) + return -EINVAL; + authmode = padapter->securitypriv.ndisauthtype; + spin_lock_irqsave(&queue->lock, irqL); + phead = get_list_head(queue); + pmlmepriv->pscanned = get_next(phead); + while (1) { + if (end_of_queue_search(phead, pmlmepriv->pscanned) == true) + break; + pnetwork = LIST_CONTAINOR(pmlmepriv->pscanned, + struct wlan_network, list); + pmlmepriv->pscanned = get_next(pmlmepriv->pscanned); + dst_bssid = pnetwork->network.MacAddress; + if (!memcmp(dst_bssid, temp->sa_data, ETH_ALEN)) { + if (r8712_set_802_11_infrastructure_mode(padapter, + pnetwork->network.InfrastructureMode) == false) + ret = -1; + break; + } + } + spin_unlock_irqrestore(&queue->lock, irqL); + if (!ret) { + if (!r8712_set_802_11_authentication_mode(padapter, authmode)) + ret = -1; + else { + if (!r8712_set_802_11_bssid(padapter, temp->sa_data)) + ret = -1; + } + } + return ret; +} + +static int r8711_wx_get_wap(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct _adapter *padapter = (struct _adapter *)_netdev_priv(dev); + struct mlme_priv *pmlmepriv = &padapter->mlmepriv; + struct ndis_wlan_bssid_ex *pcur_bss = &pmlmepriv->cur_network.network; + + wrqu->ap_addr.sa_family = ARPHRD_ETHER; + memset(wrqu->ap_addr.sa_data, 0, ETH_ALEN); + if (check_fwstate(pmlmepriv, _FW_LINKED | + WIFI_ADHOC_MASTER_STATE|WIFI_AP_STATE)) { + memcpy(wrqu->ap_addr.sa_data, pcur_bss->MacAddress, ETH_ALEN); + } + return 0; +} + +static int r871x_wx_set_mlme(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + int ret = 0; + u16 reason; + struct _adapter *padapter = (struct _adapter *)_netdev_priv(dev); + struct iw_mlme *mlme = (struct iw_mlme *) extra; + + if (mlme == NULL) + return -1; + reason = cpu_to_le16(mlme->reason_code); + switch (mlme->cmd) { + case IW_MLME_DEAUTH: + if (!r8712_set_802_11_disassociate(padapter)) + ret = -1; + break; + case IW_MLME_DISASSOC: + if (!r8712_set_802_11_disassociate(padapter)) + ret = -1; + break; + default: + return -EOPNOTSUPP; + } + return ret; +} + +static int r8711_wx_set_scan(struct net_device *dev, + struct iw_request_info *a, + union iwreq_data *wrqu, char *extra) +{ + struct _adapter *padapter = (struct _adapter *)_netdev_priv(dev); + struct mlme_priv *pmlmepriv = &padapter->mlmepriv; + u8 status = true; + + if (padapter->bDriverStopped == true) { + printk(KERN_WARNING "r8712u: in r8711_wx_set_scan: " + "bDriverStopped=%d\n", padapter->bDriverStopped); + return -1; + } + if (padapter->bup == false) + return -1; + if (padapter->hw_init_completed == false) + return -1; + if ((check_fwstate(pmlmepriv, _FW_UNDER_SURVEY|_FW_UNDER_LINKING)) || + (pmlmepriv->sitesurveyctrl.traffic_busy == true)) + return 0; + if (wrqu->data.length == sizeof(struct iw_scan_req)) { + struct iw_scan_req *req = (struct iw_scan_req *)extra; + if (wrqu->data.flags & IW_SCAN_THIS_ESSID) { + struct ndis_802_11_ssid ssid; + unsigned long irqL; + u32 len = (u32) min((u8)req->essid_len, + (u8)IW_ESSID_MAX_SIZE); + memset((unsigned char *)&ssid, 0, + sizeof(struct ndis_802_11_ssid)); + memcpy(ssid.Ssid, req->essid, len); + ssid.SsidLength = len; + spin_lock_irqsave(&pmlmepriv->lock, irqL); + if ((check_fwstate(pmlmepriv, _FW_UNDER_SURVEY | + _FW_UNDER_LINKING)) || + (pmlmepriv->sitesurveyctrl.traffic_busy == true)) { + if (check_fwstate(pmlmepriv, _FW_UNDER_LINKING)) + status = false; + } else + status = r8712_sitesurvey_cmd(padapter, &ssid); + spin_unlock_irqrestore(&pmlmepriv->lock, irqL); + } + } else + status = r8712_set_802_11_bssid_list_scan(padapter); + if (status == false) + return -1; + return 0; +} + +static int r8711_wx_get_scan(struct net_device *dev, + struct iw_request_info *a, + union iwreq_data *wrqu, char *extra) +{ + struct _adapter *padapter = (struct _adapter *)_netdev_priv(dev); + struct mlme_priv *pmlmepriv = &padapter->mlmepriv; + struct __queue *queue = &pmlmepriv->scanned_queue; + struct wlan_network *pnetwork = NULL; + unsigned long irqL; + struct list_head *plist, *phead; + char *ev = extra; + char *stop = ev + wrqu->data.length; + u32 ret = 0, cnt = 0; + + if (padapter->bDriverStopped) + return -EINVAL; + while (check_fwstate(pmlmepriv, _FW_UNDER_SURVEY|_FW_UNDER_LINKING)) { + msleep(30); + cnt++; + if (cnt > 1000) + break; + } + spin_lock_irqsave(&queue->lock, irqL); + phead = get_list_head(queue); + plist = get_next(phead); + while (1) { + if (end_of_queue_search(phead, plist) == true) + break; + if ((stop - ev) < SCAN_ITEM_SIZE) { + ret = -E2BIG; + break; + } + pnetwork = LIST_CONTAINOR(plist, struct wlan_network, list); + ev = translate_scan(padapter, a, pnetwork, ev, stop); + plist = get_next(plist); + } + spin_unlock_irqrestore(&queue->lock, irqL); + wrqu->data.length = ev - extra; + wrqu->data.flags = 0; + return ret; +} + +/* set ssid flow + * s1. set_802_11_infrastructure_mode() + * s2. set_802_11_authenticaion_mode() + * s3. set_802_11_encryption_mode() + * s4. set_802_11_ssid() + */ +static int r8711_wx_set_essid(struct net_device *dev, + struct iw_request_info *a, + union iwreq_data *wrqu, char *extra) +{ + struct _adapter *padapter = (struct _adapter *)_netdev_priv(dev); + struct mlme_priv *pmlmepriv = &padapter->mlmepriv; + struct __queue *queue = &pmlmepriv->scanned_queue; + struct wlan_network *pnetwork = NULL; + enum NDIS_802_11_AUTHENTICATION_MODE authmode; + struct ndis_802_11_ssid ndis_ssid; + u8 *dst_ssid, *src_ssid; + struct list_head *phead; + u32 len; + + if (padapter->bup == false) + return -1; + if (check_fwstate(pmlmepriv, _FW_UNDER_SURVEY)) + return -1; + if (check_fwstate(pmlmepriv, _FW_UNDER_LINKING)) + return 0; + if (wrqu->essid.length > IW_ESSID_MAX_SIZE) + return -E2BIG; + authmode = padapter->securitypriv.ndisauthtype; + if (wrqu->essid.flags && wrqu->essid.length) { + len = (wrqu->essid.length < IW_ESSID_MAX_SIZE) ? + wrqu->essid.length : IW_ESSID_MAX_SIZE; + memset(&ndis_ssid, 0, sizeof(struct ndis_802_11_ssid)); + ndis_ssid.SsidLength = len; + memcpy(ndis_ssid.Ssid, extra, len); + src_ssid = ndis_ssid.Ssid; + phead = get_list_head(queue); + pmlmepriv->pscanned = get_next(phead); + while (1) { + if (end_of_queue_search(phead, pmlmepriv->pscanned)) + break; + pnetwork = LIST_CONTAINOR(pmlmepriv->pscanned, + struct wlan_network, list); + pmlmepriv->pscanned = get_next(pmlmepriv->pscanned); + dst_ssid = pnetwork->network.Ssid.Ssid; + if ((!memcmp(dst_ssid, src_ssid, ndis_ssid.SsidLength)) + && (pnetwork->network.Ssid.SsidLength == + ndis_ssid.SsidLength)) { + if (!r8712_set_802_11_infrastructure_mode( + padapter, + pnetwork->network.InfrastructureMode)) + return -1; + break; + } + } + r8712_set_802_11_authentication_mode(padapter, authmode); + r8712_set_802_11_ssid(padapter, &ndis_ssid); + } + return -EINPROGRESS; +} + +static int r8711_wx_get_essid(struct net_device *dev, + struct iw_request_info *a, + union iwreq_data *wrqu, char *extra) +{ + struct _adapter *padapter = (struct _adapter *)_netdev_priv(dev); + struct mlme_priv *pmlmepriv = &padapter->mlmepriv; + struct ndis_wlan_bssid_ex *pcur_bss = &pmlmepriv->cur_network.network; + u32 len, ret = 0; + + if (check_fwstate(pmlmepriv, _FW_LINKED|WIFI_ADHOC_MASTER_STATE)) { + len = pcur_bss->Ssid.SsidLength; + wrqu->essid.length = len; + memcpy(extra, pcur_bss->Ssid.Ssid, len); + wrqu->essid.flags = 1; + } else + ret = -1; + return ret; +} + +static int r8711_wx_set_rate(struct net_device *dev, + struct iw_request_info *a, + union iwreq_data *wrqu, char *extra) +{ + struct _adapter *padapter = (struct _adapter *)_netdev_priv(dev); + u32 target_rate = wrqu->bitrate.value; + u32 fixed = wrqu->bitrate.fixed; + u32 ratevalue = 0; + u8 datarates[NumRates]; + u8 mpdatarate[NumRates] = {11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0xff}; + int i, ret = 0; + + if (target_rate == -1) { + ratevalue = 11; + goto set_rate; + } + target_rate = target_rate / 100000; + switch (target_rate) { + case 10: + ratevalue = 0; + break; + case 20: + ratevalue = 1; + break; + case 55: + ratevalue = 2; + break; + case 60: + ratevalue = 3; + break; + case 90: + ratevalue = 4; + break; + case 110: + ratevalue = 5; + break; + case 120: + ratevalue = 6; + break; + case 180: + ratevalue = 7; + break; + case 240: + ratevalue = 8; + break; + case 360: + ratevalue = 9; + break; + case 480: + ratevalue = 10; + break; + case 540: + ratevalue = 11; + break; + default: + ratevalue = 11; + break; + } +set_rate: + for (i = 0; i < NumRates; i++) { + if (ratevalue == mpdatarate[i]) { + datarates[i] = mpdatarate[i]; + if (fixed == 0) + break; + } else + datarates[i] = 0xff; + } + if (r8712_setdatarate_cmd(padapter, datarates) != _SUCCESS) + ret = -1; + return ret; +} + +static int r8711_wx_get_rate(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct _adapter *padapter = (struct _adapter *)_netdev_priv(dev); + struct mlme_priv *pmlmepriv = &padapter->mlmepriv; + struct ndis_wlan_bssid_ex *pcur_bss = &pmlmepriv->cur_network.network; + struct ieee80211_ht_cap *pht_capie; + int i; + u8 *p; + u16 rate, max_rate = 0, ht_cap = false; + u32 ht_ielen = 0; + u8 bw_40MHz = 0, short_GI = 0; + u16 mcs_rate = 0; + + i = 0; + if (check_fwstate(pmlmepriv, _FW_LINKED|WIFI_ADHOC_MASTER_STATE)) { + p = r8712_get_ie(&pcur_bss->IEs[12], + _HT_CAPABILITY_IE_, &ht_ielen, + pcur_bss->IELength - 12); + if (p && ht_ielen > 0) { + ht_cap = true; + pht_capie = (struct ieee80211_ht_cap *)(p + 2); + memcpy(&mcs_rate , pht_capie->supp_mcs_set, 2); + bw_40MHz = (pht_capie->cap_info & + IEEE80211_HT_CAP_SUP_WIDTH) ? 1 : 0; + short_GI = (pht_capie->cap_info & + (IEEE80211_HT_CAP_SGI_20 | + IEEE80211_HT_CAP_SGI_40)) ? 1 : 0; + } + while ((pcur_bss->SupportedRates[i] != 0) && + (pcur_bss->SupportedRates[i] != 0xFF)) { + rate = pcur_bss->SupportedRates[i] & 0x7F; + if (rate > max_rate) + max_rate = rate; + wrqu->bitrate.fixed = 0; /* no auto select */ + wrqu->bitrate.value = rate*500000; + i++; + } + if (ht_cap == true) { + if (mcs_rate & 0x8000) /* MCS15 */ + max_rate = (bw_40MHz) ? ((short_GI) ? 300 : + 270) : ((short_GI) ? 144 : 130); + else if (mcs_rate & 0x0080) /* MCS7 */ + max_rate = (bw_40MHz) ? ((short_GI) ? 150 : + 135) : ((short_GI) ? 72 : 65); + else /* default MCS7 */ + max_rate = (bw_40MHz) ? ((short_GI) ? 150 : + 135) : ((short_GI) ? 72 : 65); + max_rate *= 2; /* Mbps/2 */ + wrqu->bitrate.value = max_rate * 500000; + } else { + wrqu->bitrate.value = max_rate * 500000; + } + } else + return -1; + return 0; +} + +static int r8711_wx_get_rts(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct _adapter *padapter = (struct _adapter *)_netdev_priv(dev); + + wrqu->rts.value = padapter->registrypriv.rts_thresh; + wrqu->rts.fixed = 0; /* no auto select */ + return 0; +} + +static int r8711_wx_set_frag(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct _adapter *padapter = (struct _adapter *)_netdev_priv(dev); + + if (wrqu->frag.disabled) + padapter->xmitpriv.frag_len = MAX_FRAG_THRESHOLD; + else { + if (wrqu->frag.value < MIN_FRAG_THRESHOLD || + wrqu->frag.value > MAX_FRAG_THRESHOLD) + return -EINVAL; + padapter->xmitpriv.frag_len = wrqu->frag.value & ~0x1; + } + return 0; +} + +static int r8711_wx_get_frag(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct _adapter *padapter = (struct _adapter *) _netdev_priv(dev); + + wrqu->frag.value = padapter->xmitpriv.frag_len; + wrqu->frag.fixed = 0; /* no auto select */ + return 0; +} + +static int r8711_wx_get_retry(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + wrqu->retry.value = 7; + wrqu->retry.fixed = 0; /* no auto select */ + wrqu->retry.disabled = 1; + return 0; +} + +static int r8711_wx_set_enc(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *keybuf) +{ + u32 key; + u32 keyindex_provided; + struct NDIS_802_11_WEP wep; + enum NDIS_802_11_AUTHENTICATION_MODE authmode; + struct iw_point *erq = &(wrqu->encoding); + struct _adapter *padapter = (struct _adapter *) _netdev_priv(dev); + + key = erq->flags & IW_ENCODE_INDEX; + memset(&wep, 0, sizeof(struct NDIS_802_11_WEP)); + if (erq->flags & IW_ENCODE_DISABLED) { + printk(KERN_INFO "r8712u: r8711_wx_set_enc: " + "EncryptionDisabled\n"); + padapter->securitypriv.ndisencryptstatus = + Ndis802_11EncryptionDisabled; + padapter->securitypriv.PrivacyAlgrthm = _NO_PRIVACY_; + padapter->securitypriv.XGrpPrivacy = _NO_PRIVACY_; + padapter->securitypriv.AuthAlgrthm = 0; /* open system */ + authmode = Ndis802_11AuthModeOpen; + padapter->securitypriv.ndisauthtype = authmode; + return 0; + } + if (key) { + if (key > WEP_KEYS) + return -EINVAL; + key--; + keyindex_provided = 1; + } else { + keyindex_provided = 0; + key = padapter->securitypriv.PrivacyKeyIndex; + } + /* set authentication mode */ + if (erq->flags & IW_ENCODE_OPEN) { + printk(KERN_INFO "r8712u: r8711_wx_set_enc: " + "IW_ENCODE_OPEN\n"); + padapter->securitypriv.ndisencryptstatus = + Ndis802_11Encryption1Enabled; + padapter->securitypriv.AuthAlgrthm = 0; /* open system */ + padapter->securitypriv.PrivacyAlgrthm = _NO_PRIVACY_; + padapter->securitypriv.XGrpPrivacy = _NO_PRIVACY_; + authmode = Ndis802_11AuthModeOpen; + padapter->securitypriv.ndisauthtype = authmode; + } else if (erq->flags & IW_ENCODE_RESTRICTED) { + printk(KERN_INFO "r8712u: r8711_wx_set_enc: " + "IW_ENCODE_RESTRICTED\n"); + padapter->securitypriv.ndisencryptstatus = + Ndis802_11Encryption1Enabled; + padapter->securitypriv.AuthAlgrthm = 1; /* shared system */ + padapter->securitypriv.PrivacyAlgrthm = _WEP40_; + padapter->securitypriv.XGrpPrivacy = _WEP40_; + authmode = Ndis802_11AuthModeShared; + padapter->securitypriv.ndisauthtype = authmode; + } else { + padapter->securitypriv.ndisencryptstatus = + Ndis802_11Encryption1Enabled; + padapter->securitypriv.AuthAlgrthm = 0; /* open system */ + padapter->securitypriv.PrivacyAlgrthm = _NO_PRIVACY_; + padapter->securitypriv.XGrpPrivacy = _NO_PRIVACY_; + authmode = Ndis802_11AuthModeOpen; + padapter->securitypriv.ndisauthtype = authmode; + } + wep.KeyIndex = key; + if (erq->length > 0) { + wep.KeyLength = erq->length <= 5 ? 5 : 13; + wep.Length = wep.KeyLength + + FIELD_OFFSET(struct NDIS_802_11_WEP, KeyMaterial); + } else { + wep.KeyLength = 0 ; + if (keyindex_provided == 1) { /* set key_id only, no given + * KeyMaterial(erq->length==0).*/ + padapter->securitypriv.PrivacyKeyIndex = key; + switch (padapter->securitypriv.DefKeylen[key]) { + case 5: + padapter->securitypriv.PrivacyAlgrthm = + _WEP40_; + break; + case 13: + padapter->securitypriv.PrivacyAlgrthm = + _WEP104_; + break; + default: + padapter->securitypriv.PrivacyAlgrthm = + _NO_PRIVACY_; + break; + } + return 0; + } + } + wep.KeyIndex |= 0x80000000; /* transmit key */ + memcpy(wep.KeyMaterial, keybuf, wep.KeyLength); + if (r8712_set_802_11_add_wep(padapter, &wep) == _FAIL) + return -EOPNOTSUPP; + return 0; +} + +static int r8711_wx_get_enc(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *keybuf) +{ + uint key, ret = 0; + struct _adapter *padapter = (struct _adapter *) _netdev_priv(dev); + struct iw_point *erq = &(wrqu->encoding); + struct mlme_priv *pmlmepriv = &(padapter->mlmepriv); + + if (check_fwstate(pmlmepriv, _FW_LINKED) == false) { + if (!check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE)) { + erq->length = 0; + erq->flags |= IW_ENCODE_DISABLED; + return 0; + } + } + key = erq->flags & IW_ENCODE_INDEX; + if (key) { + if (key > WEP_KEYS) + return -EINVAL; + key--; + } else { + key = padapter->securitypriv.PrivacyKeyIndex; + } + erq->flags = key + 1; + switch (padapter->securitypriv.ndisencryptstatus) { + case Ndis802_11EncryptionNotSupported: + case Ndis802_11EncryptionDisabled: + erq->length = 0; + erq->flags |= IW_ENCODE_DISABLED; + break; + case Ndis802_11Encryption1Enabled: + erq->length = padapter->securitypriv.DefKeylen[key]; + if (erq->length) { + memcpy(keybuf, padapter->securitypriv.DefKey[ + key].skey, padapter->securitypriv. + DefKeylen[key]); + erq->flags |= IW_ENCODE_ENABLED; + if (padapter->securitypriv.ndisauthtype == + Ndis802_11AuthModeOpen) + erq->flags |= IW_ENCODE_OPEN; + else if (padapter->securitypriv.ndisauthtype == + Ndis802_11AuthModeShared) + erq->flags |= IW_ENCODE_RESTRICTED; + } else { + erq->length = 0; + erq->flags |= IW_ENCODE_DISABLED; + } + break; + case Ndis802_11Encryption2Enabled: + case Ndis802_11Encryption3Enabled: + erq->length = 16; + erq->flags |= (IW_ENCODE_ENABLED | IW_ENCODE_OPEN | + IW_ENCODE_NOKEY); + break; + default: + erq->length = 0; + erq->flags |= IW_ENCODE_DISABLED; + break; + } + return ret; +} + +static int r8711_wx_get_power(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + wrqu->power.value = 0; + wrqu->power.fixed = 0; /* no auto select */ + wrqu->power.disabled = 1; + return 0; +} + +static int r871x_wx_set_gen_ie(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct _adapter *padapter = (struct _adapter *)_netdev_priv(dev); + + return r871x_set_wpa_ie(padapter, extra, wrqu->data.length); +} + +static int r871x_wx_set_auth(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct _adapter *padapter = (struct _adapter *)_netdev_priv(dev); + struct iw_param *param = (struct iw_param *)&(wrqu->param); + int paramid; + int paramval; + int ret = 0; + + paramid = param->flags & IW_AUTH_INDEX; + paramval = param->value; + switch (paramid) { + case IW_AUTH_WPA_VERSION: + break; + case IW_AUTH_CIPHER_PAIRWISE: + break; + case IW_AUTH_CIPHER_GROUP: + break; + case IW_AUTH_KEY_MGMT: + /* + * ??? does not use these parameters + */ + break; + case IW_AUTH_TKIP_COUNTERMEASURES: + if (paramval) { + /* wpa_supplicant is enabling tkip countermeasure. */ + padapter->securitypriv.btkip_countermeasure = true; + } else { + /* wpa_supplicant is disabling tkip countermeasure. */ + padapter->securitypriv.btkip_countermeasure = false; + } + break; + case IW_AUTH_DROP_UNENCRYPTED: + /* HACK: + * + * wpa_supplicant calls set_wpa_enabled when the driver + * is loaded and unloaded, regardless of if WPA is being + * used. No other calls are made which can be used to + * determine if encryption will be used or not prior to + * association being expected. If encryption is not being + * used, drop_unencrypted is set to false, else true -- we + * can use this to determine if the CAP_PRIVACY_ON bit should + * be set. + */ + if (padapter->securitypriv.ndisencryptstatus == + Ndis802_11Encryption1Enabled) { + /* it means init value, or using wep, + * ndisencryptstatus = + * Ndis802_11Encryption1Enabled, + * then it needn't reset it; + */ + break; + } + + if (paramval) { + padapter->securitypriv.ndisencryptstatus = + Ndis802_11EncryptionDisabled; + padapter->securitypriv.PrivacyAlgrthm = + _NO_PRIVACY_; + padapter->securitypriv.XGrpPrivacy = + _NO_PRIVACY_; + padapter->securitypriv.AuthAlgrthm = 0; + padapter->securitypriv.ndisauthtype = + Ndis802_11AuthModeOpen; + } + break; + case IW_AUTH_80211_AUTH_ALG: + ret = wpa_set_auth_algs(dev, (u32)paramval); + break; + case IW_AUTH_WPA_ENABLED: + break; + case IW_AUTH_RX_UNENCRYPTED_EAPOL: + break; + case IW_AUTH_PRIVACY_INVOKED: + break; + default: + return -EOPNOTSUPP; + } + + return ret; +} + +static int r871x_wx_set_enc_ext(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct iw_point *pencoding = &wrqu->encoding; + struct iw_encode_ext *pext = (struct iw_encode_ext *)extra; + struct ieee_param *param = NULL; + char *alg_name; + u32 param_len; + int ret = 0; + + param_len = sizeof(struct ieee_param) + pext->key_len; + param = (struct ieee_param *)_malloc(param_len); + if (param == NULL) + return -1; + memset(param, 0, param_len); + param->cmd = IEEE_CMD_SET_ENCRYPTION; + memset(param->sta_addr, 0xff, ETH_ALEN); + switch (pext->alg) { + case IW_ENCODE_ALG_NONE: + alg_name = "none"; + break; + case IW_ENCODE_ALG_WEP: + alg_name = "WEP"; + break; + case IW_ENCODE_ALG_TKIP: + alg_name = "TKIP"; + break; + case IW_ENCODE_ALG_CCMP: + alg_name = "CCMP"; + break; + default: + return -1; + } + strncpy((char *)param->u.crypt.alg, alg_name, IEEE_CRYPT_ALG_NAME_LEN); + if (pext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) + param->u.crypt.set_tx = 0; + if (pext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) + param->u.crypt.set_tx = 1; + param->u.crypt.idx = (pencoding->flags & 0x00FF) - 1; + if (pext->ext_flags & IW_ENCODE_EXT_RX_SEQ_VALID) + memcpy(param->u.crypt.seq, pext->rx_seq, 8); + if (pext->key_len) { + param->u.crypt.key_len = pext->key_len; + memcpy(param + 1, pext + 1, pext->key_len); + } + ret = wpa_set_encryption(dev, param, param_len); + if (param) + kfree((u8 *)param); + return ret; +} + +static int r871x_wx_get_nick(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + if (extra) { + wrqu->data.length = 8; + wrqu->data.flags = 1; + memcpy(extra, "rtl_wifi", 8); + } + return 0; +} + +static int r8711_wx_read32(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *keybuf) +{ + struct _adapter *padapter = (struct _adapter *) _netdev_priv(dev); + u32 addr; + u32 data32; + + get_user(addr, (u32 __user *)wrqu->data.pointer); + data32 = r8712_read32(padapter, addr); + put_user(data32, (u32 __user *)wrqu->data.pointer); + wrqu->data.length = (data32 & 0xffff0000) >> 16; + wrqu->data.flags = data32 & 0xffff; + get_user(addr, (u32 __user *)wrqu->data.pointer); + return 0; +} + +static int r8711_wx_write32(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *keybuf) +{ + struct _adapter *padapter = (struct _adapter *) _netdev_priv(dev); + u32 addr; + u32 data32; + + get_user(addr, (u32 __user *)wrqu->data.pointer); + data32 = ((u32)wrqu->data.length<<16) | (u32)wrqu->data.flags ; + r8712_write32(padapter, addr, data32); + return 0; +} + +static int dummy(struct net_device *dev, + struct iw_request_info *a, + union iwreq_data *wrqu, char *b) +{ + return -1; +} + +static int r8711_drvext_hdl(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + return 0; +} + +static int r871x_mp_ioctl_hdl(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct _adapter *padapter = (struct _adapter *)_netdev_priv(dev); + struct iw_point *p = &wrqu->data; + struct oid_par_priv oid_par; + struct mp_ioctl_handler *phandler; + struct mp_ioctl_param *poidparam; + unsigned long BytesRead, BytesWritten, BytesNeeded; + u8 *pparmbuf = NULL, bset; + u16 len; + uint status; + int ret = 0; + + if ((!p->length) || (!p->pointer)) { + ret = -EINVAL; + goto _r871x_mp_ioctl_hdl_exit; + } + bset = (u8)(p->flags & 0xFFFF); + len = p->length; + pparmbuf = NULL; + pparmbuf = (u8 *)_malloc(len); + if (pparmbuf == NULL) { + ret = -ENOMEM; + goto _r871x_mp_ioctl_hdl_exit; + } + if (copy_from_user(pparmbuf, p->pointer, len)) { + ret = -EFAULT; + goto _r871x_mp_ioctl_hdl_exit; + } + poidparam = (struct mp_ioctl_param *)pparmbuf; + if (poidparam->subcode >= MAX_MP_IOCTL_SUBCODE) { + ret = -EINVAL; + goto _r871x_mp_ioctl_hdl_exit; + } + phandler = mp_ioctl_hdl + poidparam->subcode; + if ((phandler->paramsize != 0) && + (poidparam->len < phandler->paramsize)) { + ret = -EINVAL; + goto _r871x_mp_ioctl_hdl_exit; + } + if (phandler->oid == 0 && phandler->handler) + status = phandler->handler(&oid_par); + else if (phandler->handler) { + oid_par.adapter_context = padapter; + oid_par.oid = phandler->oid; + oid_par.information_buf = poidparam->data; + oid_par.information_buf_len = poidparam->len; + oid_par.dbg = 0; + BytesWritten = 0; + BytesNeeded = 0; + if (bset) { + oid_par.bytes_rw = &BytesRead; + oid_par.bytes_needed = &BytesNeeded; + oid_par.type_of_oid = SET_OID; + } else { + oid_par.bytes_rw = &BytesWritten; + oid_par.bytes_needed = &BytesNeeded; + oid_par.type_of_oid = QUERY_OID; + } + status = phandler->handler(&oid_par); + /* todo:check status, BytesNeeded, etc. */ + } else { + printk(KERN_INFO "r8712u: r871x_mp_ioctl_hdl(): err!," + " subcode=%d, oid=%d, handler=%p\n", + poidparam->subcode, phandler->oid, phandler->handler); + ret = -EFAULT; + goto _r871x_mp_ioctl_hdl_exit; + } + if (bset == 0x00) { /* query info */ + if (copy_to_user(p->pointer, pparmbuf, len)) + ret = -EFAULT; + } + if (status) { + ret = -EFAULT; + goto _r871x_mp_ioctl_hdl_exit; + } +_r871x_mp_ioctl_hdl_exit: + if (pparmbuf != NULL) + kfree(pparmbuf); + return ret; +} + +static int r871x_get_ap_info(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct _adapter *padapter = (struct _adapter *)_netdev_priv(dev); + struct mlme_priv *pmlmepriv = &padapter->mlmepriv; + struct __queue *queue = &pmlmepriv->scanned_queue; + struct iw_point *pdata = &wrqu->data; + struct wlan_network *pnetwork = NULL; + u32 cnt = 0, wpa_ielen; + unsigned long irqL; + struct list_head *plist, *phead; + unsigned char *pbuf; + u8 bssid[ETH_ALEN]; + char data[32]; + + if (padapter->bDriverStopped || (pdata == NULL)) + return -EINVAL; + while (check_fwstate(pmlmepriv, _FW_UNDER_SURVEY|_FW_UNDER_LINKING)) { + msleep(30); + cnt++; + if (cnt > 100) + break; + } + pdata->flags = 0; + if (pdata->length >= 32) { + if (copy_from_user(data, pdata->pointer, 32)) + return -EINVAL; + } else + return -EINVAL; + spin_lock_irqsave(&(pmlmepriv->scanned_queue.lock), irqL); + 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 (hwaddr_aton_i(data, bssid)) { + printk(KERN_INFO "r8712u: Invalid BSSID '%s'.\n", + (u8 *)data); + spin_unlock_irqrestore(&(pmlmepriv->scanned_queue.lock), + irqL); + return -EINVAL; + } + printk(KERN_INFO "r8712u: BSSID:%pM\n", bssid); + if (!memcmp(bssid, pnetwork->network.MacAddress, ETH_ALEN)) { + /* BSSID match, then check if supporting wpa/wpa2 */ + pbuf = r8712_get_wpa_ie(&pnetwork->network.IEs[12], + &wpa_ielen, pnetwork->network.IELength-12); + if (pbuf && (wpa_ielen > 0)) { + pdata->flags = 1; + break; + } + pbuf = r8712_get_wpa2_ie(&pnetwork->network.IEs[12], + &wpa_ielen, pnetwork->network.IELength-12); + if (pbuf && (wpa_ielen > 0)) { + pdata->flags = 2; + break; + } + } + plist = get_next(plist); + } + spin_unlock_irqrestore(&(pmlmepriv->scanned_queue.lock), irqL); + if (pdata->length >= 34) { + if (copy_to_user((u8 __user *)pdata->pointer + 32, + (u8 *)&pdata->flags, 1)) + return -EINVAL; + } + return 0; +} + +static int r871x_set_pid(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct _adapter *padapter = (struct _adapter *) _netdev_priv(dev); + struct iw_point *pdata = &wrqu->data; + + if ((padapter->bDriverStopped) || (pdata == NULL)) + return -EINVAL; + if (copy_from_user(&padapter->pid, pdata->pointer, sizeof(int))) + return -EINVAL; + return 0; +} + +static int r871x_wps_start(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct _adapter *padapter = (struct _adapter *)_netdev_priv(dev); + struct iw_point *pdata = &wrqu->data; + u32 u32wps_start = 0; + unsigned int uintRet = 0; + + uintRet = copy_from_user((void *)&u32wps_start, pdata->pointer, 4); + if ((padapter->bDriverStopped) || (pdata == NULL)) + return -EINVAL; + if (u32wps_start == 0) + u32wps_start = *extra; + if (u32wps_start == 1) /* WPS Start */ + padapter->ledpriv.LedControlHandler(padapter, + LED_CTL_START_WPS); + else if (u32wps_start == 2) /* WPS Stop because of wps success */ + padapter->ledpriv.LedControlHandler(padapter, + LED_CTL_STOP_WPS); + else if (u32wps_start == 3) /* WPS Stop because of wps fail */ + padapter->ledpriv.LedControlHandler(padapter, + LED_CTL_STOP_WPS_FAIL); + return 0; +} + +static int wpa_set_param(struct net_device *dev, u8 name, u32 value) +{ + struct _adapter *padapter = (struct _adapter *) _netdev_priv(dev); + + switch (name) { + case IEEE_PARAM_WPA_ENABLED: + padapter->securitypriv.AuthAlgrthm = 2; /* 802.1x */ + switch ((value)&0xff) { + case 1: /* WPA */ + padapter->securitypriv.ndisauthtype = + Ndis802_11AuthModeWPAPSK; /* WPA_PSK */ + padapter->securitypriv.ndisencryptstatus = + Ndis802_11Encryption2Enabled; + break; + case 2: /* WPA2 */ + padapter->securitypriv.ndisauthtype = + Ndis802_11AuthModeWPA2PSK; /* WPA2_PSK */ + padapter->securitypriv.ndisencryptstatus = + Ndis802_11Encryption3Enabled; + break; + } + break; + case IEEE_PARAM_TKIP_COUNTERMEASURES: + break; + case IEEE_PARAM_DROP_UNENCRYPTED: + /* HACK: + * + * wpa_supplicant calls set_wpa_enabled when the driver + * is loaded and unloaded, regardless of if WPA is being + * used. No other calls are made which can be used to + * determine if encryption will be used or not prior to + * association being expected. If encryption is not being + * used, drop_unencrypted is set to false, else true -- we + * can use this to determine if the CAP_PRIVACY_ON bit should + * be set. + */ + break; + case IEEE_PARAM_PRIVACY_INVOKED: + break; + case IEEE_PARAM_AUTH_ALGS: + return wpa_set_auth_algs(dev, value); + break; + case IEEE_PARAM_IEEE_802_1X: + break; + case IEEE_PARAM_WPAX_SELECT: + /* added for WPA2 mixed mode */ + break; + default: + return -EOPNOTSUPP; + } + return 0; +} + +static int wpa_mlme(struct net_device *dev, u32 command, u32 reason) +{ + struct _adapter *padapter = (struct _adapter *) _netdev_priv(dev); + + switch (command) { + case IEEE_MLME_STA_DEAUTH: + if (!r8712_set_802_11_disassociate(padapter)) + return -1; + break; + case IEEE_MLME_STA_DISASSOC: + if (!r8712_set_802_11_disassociate(padapter)) + return -1; + break; + default: + return -EOPNOTSUPP; + } + return 0; +} + +static int wpa_supplicant_ioctl(struct net_device *dev, struct iw_point *p) +{ + struct ieee_param *param; + int ret = 0; + struct _adapter *padapter = (struct _adapter *) _netdev_priv(dev); + + if (p->length < sizeof(struct ieee_param) || !p->pointer) + return -EINVAL; + param = (struct ieee_param *)_malloc(p->length); + if (param == NULL) + return -ENOMEM; + if (copy_from_user(param, p->pointer, p->length)) + kfree((u8 *)param); + return -EFAULT; + switch (param->cmd) { + case IEEE_CMD_SET_WPA_PARAM: + ret = wpa_set_param(dev, param->u.wpa_param.name, + param->u.wpa_param.value); + break; + case IEEE_CMD_SET_WPA_IE: + ret = r871x_set_wpa_ie(padapter, (char *)param->u.wpa_ie.data, + (u16)param->u.wpa_ie.len); + break; + case IEEE_CMD_SET_ENCRYPTION: + ret = wpa_set_encryption(dev, param, p->length); + break; + case IEEE_CMD_MLME: + ret = wpa_mlme(dev, param->u.mlme.command, + param->u.mlme.reason_code); + break; + default: + ret = -EOPNOTSUPP; + break; + } + if (ret == 0 && copy_to_user(p->pointer, param, p->length)) + ret = -EFAULT; + kfree((u8 *)param); + return ret; +} + +/* based on "driver_ipw" and for hostapd */ +int r871x_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + struct iwreq *wrq = (struct iwreq *)rq; + + switch (cmd) { + case RTL_IOCTL_WPA_SUPPLICANT: + return wpa_supplicant_ioctl(dev, &wrq->u.data); + default: + return -EOPNOTSUPP; + } + return 0; +} + +static iw_handler r8711_handlers[] = { + NULL, /* SIOCSIWCOMMIT */ + r8711_wx_get_name, /* SIOCGIWNAME */ + dummy, /* SIOCSIWNWID */ + dummy, /* SIOCGIWNWID */ + r8711_wx_set_freq, /* SIOCSIWFREQ */ + r8711_wx_get_freq, /* SIOCGIWFREQ */ + r8711_wx_set_mode, /* SIOCSIWMODE */ + r8711_wx_get_mode, /* SIOCGIWMODE */ + dummy, /* SIOCSIWSENS */ + r8711_wx_get_sens, /* SIOCGIWSENS */ + NULL, /* SIOCSIWRANGE */ + r8711_wx_get_range, /* SIOCGIWRANGE */ + r871x_wx_set_priv, /* SIOCSIWPRIV */ + NULL, /* SIOCGIWPRIV */ + NULL, /* SIOCSIWSTATS */ + NULL, /* SIOCGIWSTATS */ + dummy, /* SIOCSIWSPY */ + dummy, /* SIOCGIWSPY */ + NULL, /* SIOCGIWTHRSPY */ + NULL, /* SIOCWIWTHRSPY */ + r8711_wx_set_wap, /* SIOCSIWAP */ + r8711_wx_get_wap, /* SIOCGIWAP */ + r871x_wx_set_mlme, /* request MLME operation; + * uses struct iw_mlme */ + dummy, /* SIOCGIWAPLIST -- deprecated */ + r8711_wx_set_scan, /* SIOCSIWSCAN */ + r8711_wx_get_scan, /* SIOCGIWSCAN */ + r8711_wx_set_essid, /* SIOCSIWESSID */ + r8711_wx_get_essid, /* SIOCGIWESSID */ + dummy, /* SIOCSIWNICKN */ + r871x_wx_get_nick, /* SIOCGIWNICKN */ + NULL, /* -- hole -- */ + NULL, /* -- hole -- */ + r8711_wx_set_rate, /* SIOCSIWRATE */ + r8711_wx_get_rate, /* SIOCGIWRATE */ + dummy, /* SIOCSIWRTS */ + r8711_wx_get_rts, /* SIOCGIWRTS */ + r8711_wx_set_frag, /* SIOCSIWFRAG */ + r8711_wx_get_frag, /* SIOCGIWFRAG */ + dummy, /* SIOCSIWTXPOW */ + dummy, /* SIOCGIWTXPOW */ + dummy, /* SIOCSIWRETRY */ + r8711_wx_get_retry, /* SIOCGIWRETRY */ + r8711_wx_set_enc, /* SIOCSIWENCODE */ + r8711_wx_get_enc, /* SIOCGIWENCODE */ + dummy, /* SIOCSIWPOWER */ + r8711_wx_get_power, /* SIOCGIWPOWER */ + NULL, /*---hole---*/ + NULL, /*---hole---*/ + r871x_wx_set_gen_ie, /* SIOCSIWGENIE */ + NULL, /* SIOCGIWGENIE */ + r871x_wx_set_auth, /* SIOCSIWAUTH */ + NULL, /* SIOCGIWAUTH */ + r871x_wx_set_enc_ext, /* SIOCSIWENCODEEXT */ + NULL, /* SIOCGIWENCODEEXT */ + r871x_wx_set_pmkid, /* SIOCSIWPMKSA */ + NULL, /*---hole---*/ +}; + +static const struct iw_priv_args r8711_private_args[] = { + { + SIOCIWFIRSTPRIV + 0x0, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "read32" + }, + { + SIOCIWFIRSTPRIV + 0x1, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "write32" + }, + { + SIOCIWFIRSTPRIV + 0x2, 0, 0, "driver_ext" + }, + { + SIOCIWFIRSTPRIV + 0x3, 0, 0, "mp_ioctl" + }, + { + SIOCIWFIRSTPRIV + 0x4, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "apinfo" + }, + { + SIOCIWFIRSTPRIV + 0x5, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "setpid" + }, + { + SIOCIWFIRSTPRIV + 0x6, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "wps_start" + } +}; + +static iw_handler r8711_private_handler[] = { + r8711_wx_read32, + r8711_wx_write32, + r8711_drvext_hdl, + r871x_mp_ioctl_hdl, + r871x_get_ap_info, /*for MM DTV platform*/ + r871x_set_pid, + r871x_wps_start, +}; + +static struct iw_statistics *r871x_get_wireless_stats(struct net_device *dev) +{ + struct _adapter *padapter = (struct _adapter *) _netdev_priv(dev); + struct iw_statistics *piwstats = &padapter->iwstats; + int tmp_level = 0; + int tmp_qual = 0; + int tmp_noise = 0; + + if (check_fwstate(&padapter->mlmepriv, _FW_LINKED) != true) { + piwstats->qual.qual = 0; + piwstats->qual.level = 0; + piwstats->qual.noise = 0; + } else { + /* show percentage, we need transfer dbm to orignal value. */ + tmp_level = padapter->recvpriv.fw_rssi; + tmp_qual = padapter->recvpriv.signal; + tmp_noise = padapter->recvpriv.noise; + piwstats->qual.level = tmp_level; + piwstats->qual.qual = tmp_qual; + piwstats->qual.noise = tmp_noise; + } + piwstats->qual.updated = IW_QUAL_ALL_UPDATED; + return &padapter->iwstats; +} + +struct iw_handler_def r871x_handlers_def = { + .standard = r8711_handlers, + .num_standard = sizeof(r8711_handlers) / sizeof(iw_handler), + .private = r8711_private_handler, + .private_args = (struct iw_priv_args *)r8711_private_args, + .num_private = sizeof(r8711_private_handler) / sizeof(iw_handler), + .num_private_args = sizeof(r8711_private_args) / + sizeof(struct iw_priv_args), + .get_wireless_stats = r871x_get_wireless_stats, +}; |