/* * * Copyright (c) 2004-2007 Atheros Communications Inc. * All rights reserved. * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation; * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * * */ #include "ar6000_drv.h" static A_UINT8 bcast_mac[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; static void ar6000_set_quality(struct iw_quality *iq, A_INT8 rssi); extern unsigned int wmitimeout; extern A_WAITQUEUE_HEAD arEvent; extern wait_queue_head_t ar6000_scan_queue; /* * Encode a WPA or RSN information element as a custom * element using the hostap format. */ static u_int encode_ie(void *buf, size_t bufsize, const u_int8_t *ie, size_t ielen, const char *leader, size_t leader_len) { u_int8_t *p; int i; if (bufsize < leader_len) return 0; p = buf; memcpy(p, leader, leader_len); bufsize -= leader_len; p += leader_len; for (i = 0; i < ielen && bufsize > 2; i++) p += sprintf(p, "%02x", ie[i]); return (i == ielen ? p - (u_int8_t *)buf : 0); } void ar6000_scan_node(void *arg, bss_t *ni) { struct iw_event iwe; #if WIRELESS_EXT > 14 char buf[64*2 + 30]; #endif struct ar_giwscan_param *param; A_CHAR *current_ev; A_CHAR *end_buf; struct ieee80211_common_ie *cie; struct iw_request_info info; info.cmd = 0; info.flags = 0; param = (struct ar_giwscan_param *)arg; if (param->current_ev >= param->end_buf) { return; } if ((param->firstPass == TRUE) && ((ni->ni_cie.ie_wpa == NULL) && (ni->ni_cie.ie_rsn == NULL))) { /* * Only forward wpa bss's in first pass */ return; } if ((param->firstPass == FALSE) && ((ni->ni_cie.ie_wpa != NULL) || (ni->ni_cie.ie_rsn != NULL))) { /* * Only forward non-wpa bss's in 2nd pass */ return; } current_ev = param->current_ev; end_buf = param->end_buf; cie = &ni->ni_cie; A_MEMZERO(&iwe, sizeof(iwe)); iwe.cmd = SIOCGIWAP; iwe.u.ap_addr.sa_family = ARPHRD_ETHER; A_MEMCPY(iwe.u.ap_addr.sa_data, ni->ni_macaddr, 6); current_ev = iwe_stream_add_event(&info, current_ev, end_buf, &iwe, IW_EV_ADDR_LEN); A_MEMZERO(&iwe, sizeof(iwe)); iwe.cmd = SIOCGIWESSID; iwe.u.data.flags = 1; iwe.u.data.length = cie->ie_ssid[1]; current_ev = iwe_stream_add_point(&info, current_ev, end_buf, &iwe, &cie->ie_ssid[2]); if (cie->ie_capInfo & (IEEE80211_CAPINFO_ESS|IEEE80211_CAPINFO_IBSS)) { A_MEMZERO(&iwe, sizeof(iwe)); iwe.cmd = SIOCGIWMODE; iwe.u.mode = cie->ie_capInfo & IEEE80211_CAPINFO_ESS ? IW_MODE_MASTER : IW_MODE_ADHOC; current_ev = iwe_stream_add_event(&info, current_ev, end_buf, &iwe, IW_EV_UINT_LEN); } A_MEMZERO(&iwe, sizeof(iwe)); iwe.cmd = SIOCGIWFREQ; iwe.u.freq.m = cie->ie_chan * 100000; iwe.u.freq.e = 1; current_ev = iwe_stream_add_event(&info, current_ev, end_buf, &iwe, IW_EV_FREQ_LEN); A_MEMZERO(&iwe, sizeof(iwe)); iwe.cmd = IWEVQUAL; ar6000_set_quality(&iwe.u.qual, ni->ni_snr); current_ev = iwe_stream_add_event(&info, current_ev, end_buf, &iwe, IW_EV_QUAL_LEN); A_MEMZERO(&iwe, sizeof(iwe)); iwe.cmd = SIOCGIWENCODE; if (cie->ie_capInfo & IEEE80211_CAPINFO_PRIVACY) { iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; } else { iwe.u.data.flags = IW_ENCODE_DISABLED; } iwe.u.data.length = 0; current_ev = iwe_stream_add_point(&info, current_ev, end_buf, &iwe, ""); A_MEMZERO(&iwe, sizeof(iwe)); iwe.cmd = IWEVCUSTOM; snprintf(buf, sizeof(buf), "bcn_int=%d", cie->ie_beaconInt); iwe.u.data.length = strlen(buf); current_ev = iwe_stream_add_point(&info, current_ev, end_buf, &iwe, buf); if (cie->ie_wpa != NULL) { static const char wpa_leader[] = "wpa_ie="; A_MEMZERO(&iwe, sizeof(iwe)); iwe.cmd = IWEVCUSTOM; iwe.u.data.length = encode_ie(buf, sizeof(buf), cie->ie_wpa, cie->ie_wpa[1]+2, wpa_leader, sizeof(wpa_leader)-1); if (iwe.u.data.length != 0) { current_ev = iwe_stream_add_point(&info, current_ev, end_buf, &iwe, buf); } } if (cie->ie_rsn != NULL && cie->ie_rsn[0] == IEEE80211_ELEMID_RSN) { static const char rsn_leader[] = "rsn_ie="; A_MEMZERO(&iwe, sizeof(iwe)); iwe.cmd = IWEVCUSTOM; iwe.u.data.length = encode_ie(buf, sizeof(buf), cie->ie_rsn, cie->ie_rsn[1]+2, rsn_leader, sizeof(rsn_leader)-1); if (iwe.u.data.length != 0) { current_ev = iwe_stream_add_point(&info, current_ev, end_buf, &iwe, buf); } } if (cie->ie_wmm != NULL) { static const char wmm_leader[] = "wmm_ie="; A_MEMZERO(&iwe, sizeof(iwe)); iwe.cmd = IWEVCUSTOM; iwe.u.data.length = encode_ie(buf, sizeof(buf), cie->ie_wmm, cie->ie_wmm[1]+2, wmm_leader, sizeof(wmm_leader)-1); if (iwe.u.data.length != 0) { current_ev = iwe_stream_add_point(&info, current_ev, end_buf, &iwe, buf); } } if (cie->ie_ath != NULL) { static const char ath_leader[] = "ath_ie="; A_MEMZERO(&iwe, sizeof(iwe)); iwe.cmd = IWEVCUSTOM; iwe.u.data.length = encode_ie(buf, sizeof(buf), cie->ie_ath, cie->ie_ath[1]+2, ath_leader, sizeof(ath_leader)-1); if (iwe.u.data.length != 0) { current_ev = iwe_stream_add_point(&info, current_ev, end_buf, &iwe, buf); } } param->current_ev = current_ev; } int ar6000_ioctl_giwscan(struct net_device *dev, struct iw_request_info *info, struct iw_point *data, char *extra) { AR_SOFTC_T *ar = (AR_SOFTC_T *)netdev_priv(dev); struct ar_giwscan_param param; int i; if (ar->arWlanState == WLAN_DISABLED) { return -EIO; } if (ar->arWmiReady == FALSE) { return -EIO; } param.current_ev = extra; param.end_buf = extra + IW_SCAN_MAX_DATA; param.firstPass = TRUE; /* * Do two passes to insure WPA scan candidates * are sorted to the front. This is a hack to deal with * the wireless extensions capping scan results at * IW_SCAN_MAX_DATA bytes. In densely populated environments * it's easy to overflow this buffer (especially with WPA/RSN * information elements). Note this sorting hack does not * guarantee we won't overflow anyway. */ for (i = 0; i < 2; i++) { /* * Translate data to WE format. */ wmi_iterate_nodes(ar->arWmi, ar6000_scan_node, ¶m); param.firstPass = FALSE; if (param.current_ev >= param.end_buf) { data->length = param.current_ev - extra; return -E2BIG; } } data->length = param.current_ev - extra; return 0; } extern int reconnect_flag; /* SIOCSIWESSID */ static int ar6000_ioctl_siwessid(struct net_device *dev, struct iw_request_info *info, struct iw_point *data, char *ssid) { AR_SOFTC_T *ar = (AR_SOFTC_T *)netdev_priv(dev); A_STATUS status; A_UINT8 arNetworkType; if (ar->arWlanState == WLAN_DISABLED) { return -EIO; } if (ar->arWmiReady == FALSE) { return -EIO; } /* * iwconfig passes a string with length excluding any trailing NUL. * FIXME: we should be able to set an ESSID of 32 bytes, yet things fall * over badly if we do. So we limit the ESSID to 31 bytes. */ if (data->flags && (!data->length || data->length >= sizeof(ar->arSsid))) { /* * ssid is invalid */ return -EINVAL; } /* Added for bug 25178, return an IOCTL error instead of target returning Illegal parameter error when either the BSSID or channel is missing and we cannot scan during connect. */ if (data->flags) { if (ar->arSkipScan == TRUE && (ar->arChannelHint == 0 || (!ar->arReqBssid[0] && !ar->arReqBssid[1] && !ar->arReqBssid[2] && !ar->arReqBssid[3] && !ar->arReqBssid[4] && !ar->arReqBssid[5]))) { return -EINVAL; } } if (down_interruptible(&ar->arSem)) { return -ERESTARTSYS; } if (ar->arTxPending[WMI_CONTROL_PRI]) { /* * sleep until the command queue drains */ wait_event_interruptible_timeout(arEvent, ar->arTxPending[WMI_CONTROL_PRI] == 0, wmitimeout * HZ); if (signal_pending(current)) { return -EINTR; } } if (!data->flags) { arNetworkType = ar->arNetworkType; ar6000_init_profile_info(ar); ar->arNetworkType = arNetworkType; } /* * The original logic here prevented a disconnect if issuing an "essid off" * if no ESSID was set, presumably to prevent sending multiple disconnects * to the WMI. * * Unfortunately, this also meant that no disconnect was sent when we were * already connected, but the profile has been changed since (which also * clears the ESSID as a reminder that the WMI needs updating.) * * The "1 ||" makes sure we always disconnect or reconnect. The WMI doesn't * seem to mind being sent multiple disconnects. */ if (1 || (ar->arSsidLen) || (!data->flags)) { if ((!data->flags) || (A_MEMCMP(ar->arSsid, ssid, ar->arSsidLen) != 0) || (ar->arSsidLen != (data->length))) { /* * SSID set previously or essid off has been issued. * * Disconnect Command is issued in two cases after wmi is ready * (1) ssid is different from the previous setting * (2) essid off has been issued * */ if (ar->arWmiReady == TRUE) { reconnect_flag = 0; status = wmi_disconnect_cmd(ar->arWmi); A_MEMZERO(ar->arSsid, sizeof(ar->arSsid)); ar->arSsidLen = 0; if (ar->arSkipScan == FALSE) { A_MEMZERO(ar->arReqBssid, sizeof(ar->arReqBssid)); } if (!data->flags) { up(&ar->arSem); return 0; } } else { up(&ar->arSem); } } else { /* * SSID is same, so we assume profile hasn't changed. * If the interface is up and wmi is ready, we issue * a reconnect cmd. Issue a reconnect only we are already * connected. */ if((ar->arConnected == TRUE) && (ar->arWmiReady == TRUE)) { reconnect_flag = TRUE; status = wmi_reconnect_cmd(ar->arWmi,ar->arReqBssid, ar->arChannelHint); up(&ar->arSem); if (status != A_OK) { return -EIO; } return 0; } else{ /* * Dont return if connect is pending. */ if(!(ar->arConnectPending)) { up(&ar->arSem); return 0; } } } } ar->arSsidLen = data->length; A_MEMCPY(ar->arSsid, ssid, ar->arSsidLen); /* The ssid length check prevents second "essid off" from the user, to be treated as a connect cmd. The second "essid off" is ignored. */ if((ar->arWmiReady == TRUE) && (ar->arSsidLen > 0) ) { AR6000_SPIN_LOCK(&ar->arLock, 0); if (SHARED_AUTH == ar->arDot11AuthMode) { ar6000_install_static_wep_keys(ar); } AR_DEBUG_PRINTF("Connect called with authmode %d dot11 auth %d"\ " PW crypto %d PW crypto Len %d GRP crypto %d"\ " GRP crypto Len %d\n", ar->arAuthMode, ar->arDot11AuthMode, ar->arPairwiseCrypto, ar->arPairwiseCryptoLen, ar->arGroupCrypto, ar->arGroupCryptoLen); reconnect_flag = 0; AR6000_SPIN_UNLOCK(&ar->arLock, 0); status = wmi_connect_cmd(ar->arWmi, ar->arNetworkType, ar->arDot11AuthMode, ar->arAuthMode, ar->arPairwiseCrypto, ar->arPairwiseCryptoLen, ar->arGroupCrypto,ar->arGroupCryptoLen, ar->arSsidLen, ar->arSsid, ar->arReqBssid, ar->arChannelHint, ar->arConnectCtrlFlags); up(&ar->arSem); if (status != A_OK) { return -EIO; } ar->arConnectPending = TRUE; }else{ up(&ar->arSem); } return 0; } /* SIOCGIWESSID */ static int ar6000_ioctl_giwessid(struct net_device *dev, struct iw_request_info *info, struct iw_point *data, char *essid) { AR_SOFTC_T *ar = (AR_SOFTC_T *)netdev_priv(dev); if (ar->arWlanState == WLAN_DISABLED) { return -EIO; } data->flags = 1; data->length = ar->arSsidLen; A_MEMCPY(essid, ar->arSsid, ar->arSsidLen); return 0; } void ar6000_install_static_wep_keys(AR_SOFTC_T *ar) { A_UINT8 index; A_UINT8 keyUsage; for (index = WMI_MIN_KEY_INDEX; index <= WMI_MAX_KEY_INDEX; index++) { if (ar->arWepKeyList[index].arKeyLen) { keyUsage = GROUP_USAGE; if (index == ar->arDefTxKeyIndex) { keyUsage |= TX_USAGE; } wmi_addKey_cmd(ar->arWmi, index, WEP_CRYPT, keyUsage, ar->arWepKeyList[index].arKeyLen, NULL, ar->arWepKeyList[index].arKey, KEY_OP_INIT_VAL, NO_SYNC_WMIFLAG); } } } int ar6000_ioctl_delkey(struct net_device *dev, struct iw_request_info *info, void *w, char *extra) { return 0; } int ar6000_ioctl_setmlme(struct net_device *dev, struct iw_request_info *info, void *w, char *extra) { AR_SOFTC_T *ar = (AR_SOFTC_T *)netdev_priv(dev); struct ieee80211req_mlme *mlme = (struct ieee80211req_mlme *)extra; if ((ar->arWmiReady == FALSE) || (ar->arConnected != TRUE)) return -EIO; switch (mlme->im_op) { case IEEE80211_MLME_DISASSOC: case IEEE80211_MLME_DEAUTH: /* Not Supported */ break; default: break; } return 0; } int ar6000_ioctl_setwmmparams(struct net_device *dev, struct iw_request_info *info, void *w, char *extra) { return -EIO; /* for now */ } int ar6000_ioctl_getwmmparams(struct net_device *dev, struct iw_request_info *info, void *w, char *extra) { return -EIO; /* for now */ } int ar6000_ioctl_setoptie(struct net_device *dev, struct iw_request_info *info, struct iw_point *data, char *extra) { /* The target generates the WPA/RSN IE */ return 0; } int ar6000_ioctl_setauthalg(struct net_device *dev, struct iw_request_info *info, void *w, char *extra) { AR_SOFTC_T *ar = (AR_SOFTC_T *)netdev_priv(dev); struct ieee80211req_authalg *req = (struct ieee80211req_authalg *)extra; int ret = 0; AR6000_SPIN_LOCK(&ar->arLock, 0); if (req->auth_alg == AUTH_ALG_OPEN_SYSTEM) { ar->arDot11AuthMode = OPEN_AUTH; } else if (req->auth_alg == AUTH_ALG_LEAP) { ar->arDot11AuthMode = LEAP_AUTH; ar->arPairwiseCrypto = WEP_CRYPT; ar->arGroupCrypto = WEP_CRYPT; } else { ret = -EIO; } AR6000_SPIN_UNLOCK(&ar->arLock, 0); return ret; } static int ar6000_ioctl_addpmkid(struct net_device *dev, struct iw_request_info *info, void *w, char *extra) { AR_SOFTC_T *ar = (AR_SOFTC_T *)netdev_priv(dev); struct ieee80211req_addpmkid *req = (struct ieee80211req_addpmkid *)extra; A_STATUS status; if (ar->arWlanState == WLAN_DISABLED) { return -EIO; } AR_DEBUG_PRINTF("Add pmkid for %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x en=%d\n", req->pi_bssid[0], req->pi_bssid[1], req->pi_bssid[2], req->pi_bssid[3], req->pi_bssid[4], req->pi_bssid[5], req->pi_enable); status = wmi_setPmkid_cmd(ar->arWmi, req->pi_bssid, req->pi_pmkid, req->pi_enable); if (status != A_OK) { return -EIO; } return 0; } /* * SIOCSIWRATE */ int ar6000_ioctl_siwrate(struct net_device *dev, struct iw_request_info *info, struct iw_param *rrq, char *extra) { AR_SOFTC_T *ar = (AR_SOFTC_T *)netdev_priv(dev); A_UINT32 kbps; if (rrq->fixed) { kbps = rrq->value / 1000; /* rrq->value is in bps */ } else { kbps = -1; /* -1 indicates auto rate */ } if(kbps != -1 && wmi_validate_bitrate(ar->arWmi, kbps) == A_EINVAL) { AR_DEBUG_PRINTF("BitRate is not Valid %d\n", kbps); return -EINVAL; } ar->arBitRate = kbps; if(ar->arWmiReady == TRUE) { if (wmi_set_bitrate_cmd(ar->arWmi, kbps) != A_OK) { return -EINVAL; } } return 0; } /* * SIOCGIWRATE */ int ar6000_ioctl_giwrate(struct net_device *dev, struct iw_request_info *info, struct iw_param *rrq, char *extra) { AR_SOFTC_T *ar = (AR_SOFTC_T *)netdev_priv(dev); int ret = 0; if (down_interruptible(&ar->arSem)) { return -ERESTARTSYS; } if(ar->arWmiReady == TRUE) { ar->arBitRate = 0xFFFF; if (wmi_get_bitrate_cmd(ar->arWmi) != A_OK) { up(&ar->arSem); return -EIO; } wait_event_interruptible_timeout(arEvent, ar->arBitRate != 0xFFFF, wmitimeout * HZ); if (signal_pending(current)) { ret = -EINTR; } } /* If the interface is down or wmi is not ready or the target is not connected - return the value stored in the device structure */ if (!ret) { if (ar->arBitRate == -1) { rrq->fixed = TRUE; rrq->value = 0; } else { rrq->value = ar->arBitRate * 1000; } } up(&ar->arSem); return ret; } /* * SIOCSIWTXPOW */ static int ar6000_ioctl_siwtxpow(struct net_device *dev, struct iw_request_info *info, struct iw_param *rrq, char *extra) { AR_SOFTC_T *ar = (AR_SOFTC_T *)netdev_priv(dev); A_UINT8 dbM; if (ar->arWlanState == WLAN_DISABLED) { return -EIO; } if (ar->arRadioSwitch == WLAN_ENABLED && rrq->disabled) { if (wmi_switch_radio(ar->arWmi, WLAN_DISABLED) < 0) return -EIO; ar->arRadioSwitch = WLAN_DISABLED; } else if (ar->arRadioSwitch == WLAN_DISABLED && !rrq->disabled) { if (wmi_switch_radio(ar->arWmi, WLAN_ENABLED) < 0) return -EIO; ar->arRadioSwitch = WLAN_ENABLED; } if (rrq->fixed) { if (rrq->flags != IW_TXPOW_DBM) { return -EOPNOTSUPP; } ar->arTxPwr= dbM = rrq->value; ar->arTxPwrSet = TRUE; } else { ar->arTxPwr = dbM = 0; ar->arTxPwrSet = FALSE; } if(ar->arWmiReady == TRUE) { AR_DEBUG_PRINTF("Set tx pwr cmd %d dbM\n", dbM); wmi_set_txPwr_cmd(ar->arWmi, dbM); } return 0; } /* * SIOCGIWTXPOW */ int ar6000_ioctl_giwtxpow(struct net_device *dev, struct iw_request_info *info, struct iw_param *rrq, char *extra) { AR_SOFTC_T *ar = (AR_SOFTC_T *)netdev_priv(dev); int ret = 0; if (ar->arWlanState == WLAN_DISABLED) { return -EIO; } if (ar->arRadioSwitch == WLAN_DISABLED) { rrq->disabled = 1; return 0; } if (down_interruptible(&ar->arSem)) { return -ERESTARTSYS; } if((ar->arWmiReady == TRUE) && (ar->arConnected == TRUE)) { ar->arTxPwr = 0; if (wmi_get_txPwr_cmd(ar->arWmi) != A_OK) { up(&ar->arSem); return -EIO; } wait_event_interruptible_timeout(arEvent, ar->arTxPwr != 0, wmitimeout * HZ); if (signal_pending(current)) { ret = -EINTR; } } /* If the interace is down or wmi is not ready or target is not connected then return value stored in the device structure */ if (!ret) { if (ar->arTxPwrSet == TRUE) { rrq->fixed = TRUE; } rrq->value = ar->arTxPwr; rrq->flags = IW_TXPOW_DBM; } up(&ar->arSem); return ret; } /* * SIOCSIWRETRY * since iwconfig only provides us with one max retry value, we use it * to apply to data frames of the BE traffic class. */ static int ar6000_ioctl_siwretry(struct net_device *dev, struct iw_request_info *info, struct iw_param *rrq, char *extra) { AR_SOFTC_T *ar = (AR_SOFTC_T *)netdev_priv(dev); if (ar->arWlanState == WLAN_DISABLED) { return -EIO; } if (rrq->disabled) { return -EOPNOTSUPP; } if ((rrq->flags & IW_RETRY_TYPE) != IW_RETRY_LIMIT) { return -EOPNOTSUPP; } if ( !(rrq->value >= WMI_MIN_RETRIES) || !(rrq->value <= WMI_MAX_RETRIES)) { return - EINVAL; } if(ar->arWmiReady == TRUE) { if (wmi_set_retry_limits_cmd(ar->arWmi, DATA_FRAMETYPE, WMM_AC_BE, rrq->value, 0) != A_OK){ return -EINVAL; } } ar->arMaxRetries = rrq->value; return 0; } /* * SIOCGIWRETRY */ static int ar6000_ioctl_giwretry(struct net_device *dev, struct iw_request_info *info, struct iw_param *rrq, char *extra) { AR_SOFTC_T *ar = (AR_SOFTC_T *)netdev_priv(dev); if (ar->arWlanState == WLAN_DISABLED) { return -EIO; } rrq->disabled = 0; switch (rrq->flags & IW_RETRY_TYPE) { case IW_RETRY_LIFETIME: return -EOPNOTSUPP; break; case IW_RETRY_LIMIT: rrq->flags = IW_RETRY_LIMIT; switch (rrq->flags & IW_RETRY_MODIFIER) { case IW_RETRY_MIN: rrq->flags |= IW_RETRY_MIN; rrq->value = WMI_MIN_RETRIES; break; case IW_RETRY_MAX: rrq->flags |= IW_RETRY_MAX; rrq->value = ar->arMaxRetries; break; } break; } return 0; } /* * SIOCSIWENCODE */ static int ar6000_ioctl_siwencode(struct net_device *dev, struct iw_request_info *info, struct iw_point *erq, char *keybuf) { AR_SOFTC_T *ar = (AR_SOFTC_T *)netdev_priv(dev); int index; A_INT32 auth = ar->arDot11AuthMode; if (ar->arWlanState == WLAN_DISABLED) { return -EIO; } index = erq->flags & IW_ENCODE_INDEX; if (index && (((index - 1) < WMI_MIN_KEY_INDEX) || ((index - 1) > WMI_MAX_KEY_INDEX))) { return -EIO; } if (erq->flags & IW_ENCODE_DISABLED) { /* * Encryption disabled */ if (index) { /* * If key index was specified then clear the specified key */ index--; A_MEMZERO(ar->arWepKeyList[index].arKey, sizeof(ar->arWepKeyList[index].arKey)); ar->arWepKeyList[index].arKeyLen = 0; } ar->arDot11AuthMode = OPEN_AUTH; ar->arPairwiseCrypto = NONE_CRYPT; ar->arGroupCrypto = NONE_CRYPT; ar->arAuthMode = NONE_AUTH; } else { /* * Enabling WEP encryption */ if (index) { index--; /* keyindex is off base 1 in iwconfig */ } if (erq->flags & IW_ENCODE_OPEN) { auth = OPEN_AUTH; } else if (erq->flags & IW_ENCODE_RESTRICTED) { auth = SHARED_AUTH; } if (erq->length) { if (!IEEE80211_IS_VALID_WEP_CIPHER_LEN(erq->length)) { return -EIO; } A_MEMZERO(ar->arWepKeyList[index].arKey, sizeof(ar->arWepKeyList[index].arKey)); A_MEMCPY(ar->arWepKeyList[index].arKey, keybuf, erq->length); ar->arWepKeyList[index].arKeyLen = erq->length; } else { if (ar->arWepKeyList[index].arKeyLen == 0) { return -EIO; } ar->arDefTxKeyIndex = index; } ar->arPairwiseCrypto = WEP_CRYPT; ar->arGroupCrypto = WEP_CRYPT; ar->arDot11AuthMode = auth; ar->arAuthMode = NONE_AUTH; } /* * profile has changed. Erase ssid to signal change */ A_MEMZERO(ar->arSsid, sizeof(ar->arSsid)); ar->arSsidLen = 0; return 0; } static int ar6000_ioctl_giwencode(struct net_device *dev, struct iw_request_info *info, struct iw_point *erq, char *key) { AR_SOFTC_T *ar = (AR_SOFTC_T *)netdev_priv(dev); A_UINT8 keyIndex; struct ar_wep_key *wk; if (ar->arWlanState == WLAN_DISABLED) { return -EIO; } if (ar->arPairwiseCrypto == NONE_CRYPT) { erq->length = 0; erq->flags = IW_ENCODE_DISABLED; } else { /* get the keyIndex */ keyIndex = erq->flags & IW_ENCODE_INDEX; if (0 == keyIndex) { keyIndex = ar->arDefTxKeyIndex; } else if ((keyIndex - 1 < WMI_MIN_KEY_INDEX) || (keyIndex - 1 > WMI_MAX_KEY_INDEX)) { keyIndex = WMI_MIN_KEY_INDEX; } else { keyIndex--; } erq->flags = keyIndex + 1; erq->flags |= IW_ENCODE_ENABLED; wk = &ar->arWepKeyList[keyIndex]; if (erq->length > wk->arKeyLen) { erq->length = wk->arKeyLen; } if (wk->arKeyLen) { A_MEMCPY(key, wk->arKey, erq->length); } if (ar->arDot11AuthMode == OPEN_AUTH) { erq->flags |= IW_ENCODE_OPEN; } else if (ar->arDot11AuthMode == SHARED_AUTH) { erq->flags |= IW_ENCODE_RESTRICTED; } } return 0; } static int ar6000_ioctl_siwpower(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { AR_SOFTC_T *ar = (AR_SOFTC_T *)netdev_priv(dev); WMI_POWER_MODE power_mode; if (wrqu->power.disabled) power_mode = MAX_PERF_POWER; else power_mode = REC_POWER; if (wmi_powermode_cmd(ar->arWmi, power_mode) < 0) return -EIO; return 0; } static int ar6000_ioctl_giwpower(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { AR_SOFTC_T *ar = (AR_SOFTC_T *)netdev_priv(dev); /* * FIXME: * https://docs.openmoko.org/trac/ticket/2267 * When starting wpa_supplicant the kernel oopses. * The following condition avoids the oops. * Remove this comment to bless this solution. */ if (ar->arWlanState == WLAN_DISABLED || ar->arWmiReady == FALSE) return -EIO; return wmi_get_power_mode_cmd(ar->arWmi); } static int ar6000_ioctl_siwgenie(struct net_device *dev, struct iw_request_info *info, struct iw_point *dwrq, char *extra) { /* The target does that for us */ return 0; } static int ar6000_ioctl_giwgenie(struct net_device *dev, struct iw_request_info *info, struct iw_point *dwrq, char *extra) { return 0; } static int ar6000_ioctl_siwauth(struct net_device *dev, struct iw_request_info *info, struct iw_param *param, char *extra) { AR_SOFTC_T *ar = (AR_SOFTC_T *)netdev_priv(dev); int reset = 0; switch (param->flags & IW_AUTH_INDEX) { case IW_AUTH_WPA_VERSION: if (param->value & IW_AUTH_WPA_VERSION_DISABLED) { ar->arAuthMode = NONE_AUTH; } if (param->value & IW_AUTH_WPA_VERSION_WPA) { ar->arAuthMode = WPA_AUTH; } if (param->value & IW_AUTH_WPA_VERSION_WPA2) { ar->arAuthMode = WPA2_AUTH; } reset = 1; break; case IW_AUTH_CIPHER_PAIRWISE: if (param->value & IW_AUTH_CIPHER_NONE) { ar->arPairwiseCrypto = NONE_CRYPT; } if (param->value & IW_AUTH_CIPHER_WEP40) { ar->arPairwiseCrypto = WEP_CRYPT; } if (param->value & IW_AUTH_CIPHER_TKIP) { ar->arPairwiseCrypto = TKIP_CRYPT; } if (param->value & IW_AUTH_CIPHER_CCMP) { ar->arPairwiseCrypto = AES_CRYPT; } reset = 1; break; case IW_AUTH_CIPHER_GROUP: if (param->value & IW_AUTH_CIPHER_NONE) { ar->arGroupCrypto = NONE_CRYPT; } if (param->value & IW_AUTH_CIPHER_WEP40) { ar->arGroupCrypto = WEP_CRYPT; } if (param->value & IW_AUTH_CIPHER_TKIP) { ar->arGroupCrypto = TKIP_CRYPT; } if (param->value & IW_AUTH_CIPHER_CCMP) { ar->arGroupCrypto = AES_CRYPT; } reset = 1; break; case IW_AUTH_KEY_MGMT: if (param->value & IW_AUTH_KEY_MGMT_PSK) { if (ar->arAuthMode == WPA_AUTH) { ar->arAuthMode = WPA_PSK_AUTH; } else if (ar->arAuthMode == WPA2_AUTH) { ar->arAuthMode = WPA2_PSK_AUTH; } reset = 1; } break; case IW_AUTH_TKIP_COUNTERMEASURES: if (ar->arWmiReady == FALSE) { return -EIO; } wmi_set_tkip_countermeasures_cmd(ar->arWmi, param->value); break; case IW_AUTH_DROP_UNENCRYPTED: break; case IW_AUTH_80211_AUTH_ALG: if (param->value & IW_AUTH_ALG_OPEN_SYSTEM) { ar->arDot11AuthMode = OPEN_AUTH; } if (param->value & IW_AUTH_ALG_SHARED_KEY) { ar->arDot11AuthMode = SHARED_AUTH; } if (param->value & IW_AUTH_ALG_LEAP) { ar->arDot11AuthMode = LEAP_AUTH; ar->arPairwiseCrypto = WEP_CRYPT; ar->arGroupCrypto = WEP_CRYPT; } reset = 1; break; case IW_AUTH_WPA_ENABLED: reset = 1; break; case IW_AUTH_RX_UNENCRYPTED_EAPOL: break; case IW_AUTH_PRIVACY_INVOKED: break; default: printk("%s(): Unknown flag 0x%x\n", __FUNCTION__, param->flags); return -EOPNOTSUPP; } if (reset) { A_MEMZERO(ar->arSsid, sizeof(ar->arSsid)); ar->arSsidLen = 0; } return 0; } static int ar6000_ioctl_giwauth(struct net_device *dev, struct iw_request_info *info, struct iw_param *dwrq, char *extra) { return 0; } static int ar6000_ioctl_siwencodeext(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { AR_SOFTC_T *ar = (AR_SOFTC_T *)netdev_priv(dev); struct iw_point *encoding = &wrqu->encoding; struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; int alg = ext->alg, idx; if (ar->arWlanState == WLAN_DISABLED) { return -EIO; } /* Determine and validate the key index */ idx = (encoding->flags & IW_ENCODE_INDEX) - 1; if (idx) { if (idx < 0 || idx > 3) return -EINVAL; } if ((alg == IW_ENCODE_ALG_TKIP) || (alg == IW_ENCODE_ALG_CCMP)) { struct ieee80211req_key ik; KEY_USAGE key_usage; CRYPTO_TYPE key_type = NONE_CRYPT; int status; ar->user_saved_keys.keyOk = FALSE; if (alg == IW_ENCODE_ALG_TKIP) { key_type = TKIP_CRYPT; ik.ik_type = IEEE80211_CIPHER_TKIP; } else { key_type = AES_CRYPT; ik.ik_type = IEEE80211_CIPHER_AES_CCM; } ik.ik_keyix = idx; ik.ik_keylen = ext->key_len; ik.ik_flags = IEEE80211_KEY_RECV; if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) { ik.ik_flags |= IEEE80211_KEY_XMIT | IEEE80211_KEY_DEFAULT; } if (ext->ext_flags & IW_ENCODE_EXT_RX_SEQ_VALID) { memcpy(&ik.ik_keyrsc, ext->rx_seq, 8); } memcpy(ik.ik_keydata, ext->key, ext->key_len); ar->user_saved_keys.keyType = key_type; if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) { key_usage = GROUP_USAGE; memset(ik.ik_macaddr, 0, ETH_ALEN); memcpy(&ar->user_saved_keys.bcast_ik, &ik, sizeof(struct ieee80211req_key)); } else { key_usage = PAIRWISE_USAGE; memcpy(ik.ik_macaddr, ext->addr.sa_data, ETH_ALEN); memcpy(&ar->user_saved_keys.ucast_ik, &ik, sizeof(struct ieee80211req_key)); } status = wmi_addKey_cmd(ar->arWmi, ik.ik_keyix, key_type, key_usage, ik.ik_keylen, (A_UINT8 *)&ik.ik_keyrsc, ik.ik_keydata, KEY_OP_INIT_VAL, SYNC_BEFORE_WMIFLAG); if (status < 0) return -EIO; ar->user_saved_keys.keyOk = TRUE; return 0; } else { /* WEP falls back to SIWENCODE */ return -EOPNOTSUPP; } return 0; } static int ar6000_ioctl_giwencodeext(struct net_device *dev, struct iw_request_info *info, struct iw_point *dwrq, char *extra) { return 0; } static int ar6000_ioctl_setparam(struct net_device *dev, struct iw_request_info *info, void *erq, char *extra) { AR_SOFTC_T *ar = (AR_SOFTC_T *)netdev_priv(dev); int *i = (int *)extra; int param = i[0]; int value = i[1]; int ret = 0; A_BOOL profChanged = FALSE; if (ar->arWlanState == WLAN_DISABLED) { return -EIO; } switch (param) { case IEEE80211_PARAM_WPA: switch (value) { case WPA_MODE_WPA1: ar->arAuthMode = WPA_AUTH; profChanged = TRUE; break; case WPA_MODE_WPA2: ar->arAuthMode = WPA2_AUTH; profChanged = TRUE; break; case WPA_MODE_NONE: ar->arAuthMode = NONE_AUTH; profChanged = TRUE; break; default: printk("IEEE80211_PARAM_WPA: Unknown value %d\n", value); } break; case IEEE80211_PARAM_AUTHMODE: switch(value) { case IEEE80211_AUTH_WPA_PSK: if (WPA_AUTH == ar->arAuthMode) { ar->arAuthMode = WPA_PSK_AUTH; profChanged = TRUE; } else if (WPA2_AUTH == ar->arAuthMode) { ar->arAuthMode = WPA2_PSK_AUTH; profChanged = TRUE; } else { AR_DEBUG_PRINTF("Error - Setting PSK mode when WPA "\ "param was set to %d\n", ar->arAuthMode); ret = -1; } break; case IEEE80211_AUTH_WPA_CCKM: if (WPA2_AUTH == ar->arAuthMode) { ar->arAuthMode = WPA2_AUTH_CCKM; } else { ar->arAuthMode = WPA_AUTH_CCKM; } break; default: break; } break; case IEEE80211_PARAM_UCASTCIPHER: switch (value) { case IEEE80211_CIPHER_AES_CCM: ar->arPairwiseCrypto = AES_CRYPT; profChanged = TRUE; break; case IEEE80211_CIPHER_TKIP: ar->arPairwiseCrypto = TKIP_CRYPT; profChanged = TRUE; break; case IEEE80211_CIPHER_WEP: ar->arPairwiseCrypto = WEP_CRYPT; profChanged = TRUE; break; case IEEE80211_CIPHER_NONE: ar->arPairwiseCrypto = NONE_CRYPT; profChanged = TRUE; break; } break; case IEEE80211_PARAM_UCASTKEYLEN: if (!IEEE80211_IS_VALID_WEP_CIPHER_LEN(value)) { ret = -EIO; } else { ar->arPairwiseCryptoLen = value; } break; case IEEE80211_PARAM_MCASTCIPHER: switch (value) { case IEEE80211_CIPHER_AES_CCM: ar->arGroupCrypto = AES_CRYPT; profChanged = TRUE; break; case IEEE80211_CIPHER_TKIP: ar->arGroupCrypto = TKIP_CRYPT; profChanged = TRUE; break; case IEEE80211_CIPHER_WEP: ar->arGroupCrypto = WEP_CRYPT; profChanged = TRUE; break; case IEEE80211_CIPHER_NONE: ar->arGroupCrypto = NONE_CRYPT; profChanged = TRUE; break; } break; case IEEE80211_PARAM_MCASTKEYLEN: if (!IEEE80211_IS_VALID_WEP_CIPHER_LEN(value)) { ret = -EIO; } else { ar->arGroupCryptoLen = value; } break; case IEEE80211_PARAM_COUNTERMEASURES: if (ar->arWmiReady == FALSE) { return -EIO; } wmi_set_tkip_countermeasures_cmd(ar->arWmi, value); break; default: break; } if (profChanged == TRUE) { /* * profile has changed. Erase ssid to signal change */ A_MEMZERO(ar->arSsid, sizeof(ar->arSsid)); ar->arSsidLen = 0; } return ret; } int ar6000_ioctl_getparam(struct net_device *dev, struct iw_request_info *info, void *w, char *extra) { return -EIO; /* for now */ } int ar6000_ioctl_setkey(struct net_device *dev, struct iw_request_info *info, void *w, char *extra) { AR_SOFTC_T *ar = (AR_SOFTC_T *)netdev_priv(dev); struct ieee80211req_key *ik = (struct ieee80211req_key *)extra; KEY_USAGE keyUsage; A_STATUS status; CRYPTO_TYPE keyType = NONE_CRYPT; if (ar->arWlanState == WLAN_DISABLED) { return -EIO; } ar->user_saved_keys.keyOk = FALSE; if ( 0 == memcmp(ik->ik_macaddr, "\x00\x00\x00\x00\x00\x00", IEEE80211_ADDR_LEN)) { keyUsage = GROUP_USAGE; A_MEMCPY(&ar->user_saved_keys.bcast_ik, ik, sizeof(struct ieee80211req_key)); } else { keyUsage = PAIRWISE_USAGE; A_MEMCPY(&ar->user_saved_keys.ucast_ik, ik, sizeof(struct ieee80211req_key)); } switch (ik->ik_type) { case IEEE80211_CIPHER_WEP: keyType = WEP_CRYPT; break; case IEEE80211_CIPHER_TKIP: keyType = TKIP_CRYPT; break; case IEEE80211_CIPHER_AES_CCM: keyType = AES_CRYPT; break; default: break; } ar->user_saved_keys.keyType = keyType; if (IEEE80211_CIPHER_CCKM_KRK != ik->ik_type) { if (NONE_CRYPT == keyType) { return -EIO; } status = wmi_addKey_cmd(ar->arWmi, ik->ik_keyix, keyType, keyUsage, ik->ik_keylen, (A_UINT8 *)&ik->ik_keyrsc, ik->ik_keydata, KEY_OP_INIT_VAL, SYNC_BEFORE_WMIFLAG); if (status != A_OK) { return -EIO; } } else { status = wmi_add_krk_cmd(ar->arWmi, ik->ik_keydata); } ar->user_saved_keys.keyOk = TRUE; return 0; } /* * SIOCGIWNAME */ int ar6000_ioctl_giwname(struct net_device *dev, struct iw_request_info *info, char *name, char *extra) { AR_SOFTC_T *ar = (AR_SOFTC_T *)netdev_priv(dev); if (ar->arWlanState == WLAN_DISABLED) { return -EIO; } switch (ar->arPhyCapability) { case (WMI_11A_CAPABILITY): strncpy(name, "AR6000 802.11a", IFNAMSIZ); break; case (WMI_11G_CAPABILITY): strncpy(name, "AR6000 802.11g", IFNAMSIZ); break; case (WMI_11AG_CAPABILITY): strncpy(name, "AR6000 802.11ag", IFNAMSIZ); break; default: strncpy(name, "AR6000 802.11", IFNAMSIZ); break; } return 0; } /* * SIOCSIWFREQ */ int ar6000_ioctl_siwfreq(struct net_device *dev, struct iw_request_info *info, struct iw_freq *freq, char *extra) { AR_SOFTC_T *ar = (AR_SOFTC_T *)netdev_priv(dev); if (ar->arWlanState == WLAN_DISABLED) { return -EIO; } /* * We support limiting the channels via wmiconfig. * * We use this command to configure the channel hint for the connect cmd * so it is possible the target will end up connecting to a different * channel. */ if (freq->e > 1) { return -EINVAL; } else if (freq->e == 1) { ar->arChannelHint = freq->m / 100000; } else { ar->arChannelHint = wlan_ieee2freq(freq->m); } A_PRINTF("channel hint set to %d\n", ar->arChannelHint); return 0; } /* * SIOCGIWFREQ */ int ar6000_ioctl_giwfreq(struct net_device *dev, struct iw_request_info *info, struct iw_freq *freq, char *extra) { AR_SOFTC_T *ar = (AR_SOFTC_T *)netdev_priv(dev); if (ar->arWlanState == WLAN_DISABLED) { return -EIO; } if (ar->arConnected != TRUE) { return -EINVAL; } freq->m = ar->arBssChannel * 100000; freq->e = 1; return 0; } /* * SIOCSIWMODE */ int ar6000_ioctl_siwmode(struct net_device *dev, struct iw_request_info *info, __u32 *mode, char *extra) { AR_SOFTC_T *ar = (AR_SOFTC_T *)netdev_priv(dev); if (ar->arWlanState == WLAN_DISABLED) { return -EIO; } switch (*mode) { case IW_MODE_INFRA: ar->arNetworkType = INFRA_NETWORK; break; case IW_MODE_ADHOC: ar->arNetworkType = ADHOC_NETWORK; break; default: return -EINVAL; } return 0; } /* * SIOCGIWMODE */ int ar6000_ioctl_giwmode(struct net_device *dev, struct iw_request_info *info, __u32 *mode, char *extra) { AR_SOFTC_T *ar = (AR_SOFTC_T *)netdev_priv(dev); if (ar->arWlanState == WLAN_DISABLED) { return -EIO; } switch (ar->arNetworkType) { case INFRA_NETWORK: *mode = IW_MODE_INFRA; break; case ADHOC_NETWORK: *mode = IW_MODE_ADHOC; break; default: return -EIO; } return 0; } /* * SIOCSIWSENS */ int ar6000_ioctl_siwsens(struct net_device *dev, struct iw_request_info *info, struct iw_param *sens, char *extra) { return 0; } /* * SIOCGIWSENS */ int ar6000_ioctl_giwsens(struct net_device *dev, struct iw_request_info *info, struct iw_param *sens, char *extra) { sens->value = 0; sens->fixed = 1; return 0; } /* * SIOCGIWRANGE */ int ar6000_ioctl_giwrange(struct net_device *dev, struct iw_request_info *info, struct iw_point *data, char *extra) { AR_SOFTC_T *ar = (AR_SOFTC_T *)netdev_priv(dev); struct iw_range *range = (struct iw_range *) extra; int i, ret = 0; if (ar->arWmiReady == FALSE) { return -EIO; } if (ar->arWlanState == WLAN_DISABLED) { return -EIO; } if (down_interruptible(&ar->arSem)) { return -ERESTARTSYS; } ar->arNumChannels = -1; A_MEMZERO(ar->arChannelList, sizeof (ar->arChannelList)); if (wmi_get_channelList_cmd(ar->arWmi) != A_OK) { up(&ar->arSem); return -EIO; } wait_event_interruptible_timeout(arEvent, ar->arNumChannels != -1, wmitimeout * HZ); if (signal_pending(current)) { up(&ar->arSem); return -EINTR; } data->length = sizeof(struct iw_range); A_MEMZERO(range, sizeof(struct iw_range)); range->txpower_capa = IW_TXPOW_DBM; range->min_pmp = 1 * 1024; range->max_pmp = 65535 * 1024; range->min_pmt = 1 * 1024; range->max_pmt = 1000 * 1024; range->pmp_flags = IW_POWER_PERIOD; range->pmt_flags = IW_POWER_TIMEOUT; range->pm_capa = 0; range->we_version_compiled = WIRELESS_EXT; range->we_version_source = 13; range->retry_capa = IW_RETRY_LIMIT; range->retry_flags = IW_RETRY_LIMIT; range->min_retry = 0; range->max_retry = 255; range->num_frequency = range->num_channels = ar->arNumChannels; for (i = 0; i < ar->arNumChannels; i++) { range->freq[i].i = wlan_freq2ieee(ar->arChannelList[i]); range->freq[i].m = ar->arChannelList[i] * 100000; range->freq[i].e = 1; /* * Linux supports max of 32 channels, bail out once you * reach the max. */ if (i == IW_MAX_FREQUENCIES) { break; } } /* Max quality is max field value minus noise floor */ range->max_qual.qual = 0xff - 161; /* * In order to use dBm measurements, 'level' must be lower * than any possible measurement (see iw_print_stats() in * wireless tools). It's unclear how this is meant to be * done, but setting zero in these values forces dBm and * the actual numbers are not used. */ range->max_qual.level = 0; range->max_qual.noise = 0; range->sensitivity = 3; range->max_encoding_tokens = 4; /* XXX query driver to find out supported key sizes */ range->num_encoding_sizes = 3; range->encoding_size[0] = 5; /* 40-bit */ range->encoding_size[1] = 13; /* 104-bit */ range->encoding_size[2] = 16; /* 128-bit */ range->num_bitrates = 0; /* estimated maximum TCP throughput values (bps) */ range->throughput = 22000000; range->min_rts = 0; range->max_rts = 2347; range->min_frag = 256; range->max_frag = 2346; up(&ar->arSem); return ret; } /* * SIOCSIWAP * This ioctl is used to set the desired bssid for the connect command. */ int ar6000_ioctl_siwap(struct net_device *dev, struct iw_request_info *info, struct sockaddr *ap_addr, char *extra) { AR_SOFTC_T *ar = (AR_SOFTC_T *)netdev_priv(dev); if (ar->arWlanState == WLAN_DISABLED) { return -EIO; } if (ap_addr->sa_family != ARPHRD_ETHER) { return -EIO; } if (A_MEMCMP(&ap_addr->sa_data, bcast_mac, AR6000_ETH_ADDR_LEN) == 0) { A_MEMZERO(ar->arReqBssid, sizeof(ar->arReqBssid)); } else { A_MEMCPY(ar->arReqBssid, &ap_addr->sa_data, sizeof(ar->arReqBssid)); } return 0; } /* * SIOCGIWAP */ int ar6000_ioctl_giwap(struct net_device *dev, struct iw_request_info *info, struct sockaddr *ap_addr, char *extra) { AR_SOFTC_T *ar = (AR_SOFTC_T *)netdev_priv(dev); if (ar->arWlanState == WLAN_DISABLED) { return -EIO; } if (ar->arConnected != TRUE) { return -EINVAL; } A_MEMCPY(&ap_addr->sa_data, ar->arBssid, sizeof(ar->arBssid)); ap_addr->sa_family = ARPHRD_ETHER; return 0; } /* * SIOCGIWAPLIST */ int ar6000_ioctl_iwaplist(struct net_device *dev, struct iw_request_info *info, struct iw_point *data, char *extra) { return -EIO; /* for now */ } /* * SIOCSIWSCAN */ int ar6000_ioctl_siwscan(struct net_device *dev, struct iw_request_info *info, struct iw_point *data, char *extra) { #define ACT_DWELLTIME_DEFAULT 105 #define HOME_TXDRAIN_TIME 100 #define SCAN_INT HOME_TXDRAIN_TIME + ACT_DWELLTIME_DEFAULT AR_SOFTC_T *ar = (AR_SOFTC_T *)netdev_priv(dev); int ret = 0; if (ar->arWmiReady == FALSE) { return -EIO; } if (ar->arWlanState == WLAN_DISABLED) { return -EIO; } /* We ask for everything from the target */ if (wmi_bssfilter_cmd(ar->arWmi, ALL_BSS_FILTER, 0) != A_OK) { printk("Couldn't set filtering\n"); ret = -EIO; } if (wmi_startscan_cmd(ar->arWmi, WMI_LONG_SCAN, FALSE, FALSE, \ HOME_TXDRAIN_TIME, SCAN_INT) != A_OK) { ret = -EIO; } ar->scan_complete = 0; wait_event_interruptible_timeout(ar6000_scan_queue, ar->scan_complete, 5 * HZ); if (wmi_bssfilter_cmd(ar->arWmi, NONE_BSS_FILTER, 0) != A_OK) { printk("Couldn't set filtering\n"); ret = -EIO; } return ret; #undef ACT_DWELLTIME_DEFAULT #undef HOME_TXDRAIN_TIME #undef SCAN_INT } /* * Units are in db above the noise floor. That means the * rssi values reported in the tx/rx descriptors in the * driver are the SNR expressed in db. * * If you assume that the noise floor is -95, which is an * excellent assumption 99.5 % of the time, then you can * derive the absolute signal level (i.e. -95 + rssi). * There are some other slight factors to take into account * depending on whether the rssi measurement is from 11b, * 11g, or 11a. These differences are at most 2db and * can be documented. * * NB: various calculations are based on the orinoco/wavelan * drivers for compatibility */ static void ar6000_set_quality(struct iw_quality *iq, A_INT8 rssi) { if (rssi < 0) { iq->qual = 0; } else { iq->qual = rssi; } /* NB: max is 94 because noise is hardcoded to 161 */ if (iq->qual > 94) iq->qual = 94; iq->noise = 161; /* -95dBm */ iq->level = iq->noise + iq->qual; iq->updated = 7; } /* Structures to export the Wireless Handlers */ static const iw_handler ath_handlers[] = { (iw_handler) NULL, /* SIOCSIWCOMMIT */ (iw_handler) ar6000_ioctl_giwname, /* SIOCGIWNAME */ (iw_handler) NULL, /* SIOCSIWNWID */ (iw_handler) NULL, /* SIOCGIWNWID */ (iw_handler) ar6000_ioctl_siwfreq, /* SIOCSIWFREQ */ (iw_handler) ar6000_ioctl_giwfreq, /* SIOCGIWFREQ */ (iw_handler) ar6000_ioctl_siwmode, /* SIOCSIWMODE */ (iw_handler) ar6000_ioctl_giwmode, /* SIOCGIWMODE */ (iw_handler) ar6000_ioctl_siwsens, /* SIOCSIWSENS */ (iw_handler) ar6000_ioctl_giwsens, /* SIOCGIWSENS */ (iw_handler) NULL /* not _used */, /* SIOCSIWRANGE */ (iw_handler) ar6000_ioctl_giwrange, /* SIOCGIWRANGE */ (iw_handler) NULL /* not used */, /* SIOCSIWPRIV */ (iw_handler) NULL /* kernel code */, /* SIOCGIWPRIV */ (iw_handler) NULL /* not used */, /* SIOCSIWSTATS */ (iw_handler) NULL /* kernel code */, /* SIOCGIWSTATS */ (iw_handler) NULL, /* SIOCSIWSPY */ (iw_handler) NULL, /* SIOCGIWSPY */ (iw_handler) NULL, /* SIOCSIWTHRSPY */ (iw_handler) NULL, /* SIOCGIWTHRSPY */ (iw_handler) ar6000_ioctl_siwap, /* SIOCSIWAP */ (iw_handler) ar6000_ioctl_giwap, /* SIOCGIWAP */ (iw_handler) NULL, /* -- hole -- */ (iw_handler) ar6000_ioctl_iwaplist, /* SIOCGIWAPLIST */ (iw_handler) ar6000_ioctl_siwscan, /* SIOCSIWSCAN */ (iw_handler) ar6000_ioctl_giwscan, /* SIOCGIWSCAN */ (iw_handler) ar6000_ioctl_siwessid, /* SIOCSIWESSID */ (iw_handler) ar6000_ioctl_giwessid, /* SIOCGIWESSID */ (iw_handler) NULL, /* SIOCSIWNICKN */ (iw_handler) NULL, /* SIOCGIWNICKN */ (iw_handler) NULL, /* -- hole -- */ (iw_handler) NULL, /* -- hole -- */ (iw_handler) ar6000_ioctl_siwrate, /* SIOCSIWRATE */ (iw_handler) ar6000_ioctl_giwrate, /* SIOCGIWRATE */ (iw_handler) NULL, /* SIOCSIWRTS */ (iw_handler) NULL, /* SIOCGIWRTS */ (iw_handler) NULL, /* SIOCSIWFRAG */ (iw_handler) NULL, /* SIOCGIWFRAG */ (iw_handler) ar6000_ioctl_siwtxpow, /* SIOCSIWTXPOW */ (iw_handler) ar6000_ioctl_giwtxpow, /* SIOCGIWTXPOW */ (iw_handler) ar6000_ioctl_siwretry, /* SIOCSIWRETRY */ (iw_handler) ar6000_ioctl_giwretry, /* SIOCGIWRETRY */ (iw_handler) ar6000_ioctl_siwencode, /* SIOCSIWENCODE */ (iw_handler) ar6000_ioctl_giwencode, /* SIOCGIWENCODE */ (iw_handler) ar6000_ioctl_siwpower, /* SIOCSIWPOWER */ (iw_handler) ar6000_ioctl_giwpower, /* SIOCGIWPOWER */ (iw_handler) NULL, /* -- hole -- */ (iw_handler) NULL, /* -- hole -- */ (iw_handler) ar6000_ioctl_siwgenie, /* SIOCSIWGENIE */ (iw_handler) ar6000_ioctl_giwgenie, /* SIOCGIWGENIE */ (iw_handler) ar6000_ioctl_siwauth, /* SIOCSIWAUTH */ (iw_handler) ar6000_ioctl_giwauth, /* SIOCGIWAUTH */ (iw_handler) ar6000_ioctl_siwencodeext,/* SIOCSIWENCODEEXT */ (iw_handler) ar6000_ioctl_giwencodeext,/* SIOCGIWENCODEEXT */ (iw_handler) NULL, /* SIOCSIWPMKSA */ }; static const iw_handler ath_priv_handlers[] = { (iw_handler) ar6000_ioctl_setparam, /* SIOCWFIRSTPRIV+0 */ (iw_handler) ar6000_ioctl_getparam, /* SIOCWFIRSTPRIV+1 */ (iw_handler) ar6000_ioctl_setkey, /* SIOCWFIRSTPRIV+2 */ (iw_handler) ar6000_ioctl_setwmmparams, /* SIOCWFIRSTPRIV+3 */ (iw_handler) ar6000_ioctl_delkey, /* SIOCWFIRSTPRIV+4 */ (iw_handler) ar6000_ioctl_getwmmparams, /* SIOCWFIRSTPRIV+5 */ (iw_handler) ar6000_ioctl_setoptie, /* SIOCWFIRSTPRIV+6 */ (iw_handler) ar6000_ioctl_setmlme, /* SIOCWFIRSTPRIV+7 */ (iw_handler) ar6000_ioctl_addpmkid, /* SIOCWFIRSTPRIV+8 */ }; #define IW_PRIV_TYPE_KEY \ (IW_PRIV_TYPE_BYTE | sizeof(struct ieee80211req_key)) #define IW_PRIV_TYPE_DELKEY \ (IW_PRIV_TYPE_BYTE | sizeof(struct ieee80211req_del_key)) #define IW_PRIV_TYPE_MLME \ (IW_PRIV_TYPE_BYTE | sizeof(struct ieee80211req_mlme)) #define IW_PRIV_TYPE_ADDPMKID \ (IW_PRIV_TYPE_BYTE | sizeof(struct ieee80211req_addpmkid)) static const struct iw_priv_args ar6000_priv_args[] = { { IEEE80211_IOCTL_SETKEY, IW_PRIV_TYPE_KEY | IW_PRIV_SIZE_FIXED, 0, "setkey"}, { IEEE80211_IOCTL_DELKEY, IW_PRIV_TYPE_DELKEY | IW_PRIV_SIZE_FIXED, 0, "delkey"}, { IEEE80211_IOCTL_SETPARAM, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, 0, "setparam"}, { IEEE80211_IOCTL_GETPARAM, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getparam"}, { IEEE80211_IOCTL_SETWMMPARAMS, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 4, 0, "setwmmparams"}, { IEEE80211_IOCTL_GETWMMPARAMS, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 3, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getwmmparams"}, { IEEE80211_IOCTL_SETOPTIE, IW_PRIV_TYPE_BYTE, 0, "setie"}, { IEEE80211_IOCTL_SETMLME, IW_PRIV_TYPE_MLME, 0, "setmlme"}, { IEEE80211_IOCTL_ADDPMKID, IW_PRIV_TYPE_ADDPMKID | IW_PRIV_SIZE_FIXED, 0, "addpmkid"}, }; void ar6000_ioctl_iwsetup(struct iw_handler_def *def) { def->private_args = (struct iw_priv_args *)ar6000_priv_args; def->num_private_args = ARRAY_SIZE(ar6000_priv_args); } struct iw_handler_def ath_iw_handler_def = { .standard = (iw_handler *)ath_handlers, .num_standard = ARRAY_SIZE(ath_handlers), .private = (iw_handler *)ath_priv_handlers, .num_private = ARRAY_SIZE(ath_priv_handlers), };