| //------------------------------------------------------------------------------ |
| // Copyright (c) 2004-2010 Atheros Communications Inc. |
| // All rights reserved. |
| // |
| // |
| // |
| // Permission to use, copy, modify, and/or distribute this software for any |
| // purpose with or without fee is hereby granted, provided that the above |
| // copyright notice and this permission notice appear in all copies. |
| // |
| // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
| // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
| // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
| // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
| // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
| // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| // |
| // |
| // |
| // Author(s): ="Atheros" |
| //------------------------------------------------------------------------------ |
| |
| #include "ar6000_drv.h" |
| #include "wlan_config.h" |
| |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) |
| #define IWE_STREAM_ADD_EVENT(p1, p2, p3, p4, p5) \ |
| iwe_stream_add_event((p1), (p2), (p3), (p4), (p5)) |
| #else |
| #define IWE_STREAM_ADD_EVENT(p1, p2, p3, p4, p5) \ |
| iwe_stream_add_event((p2), (p3), (p4), (p5)) |
| #endif |
| |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) |
| #define IWE_STREAM_ADD_POINT(p1, p2, p3, p4, p5) \ |
| iwe_stream_add_point((p1), (p2), (p3), (p4), (p5)) |
| #else |
| #define IWE_STREAM_ADD_POINT(p1, p2, p3, p4, p5) \ |
| iwe_stream_add_point((p2), (p3), (p4), (p5)) |
| #endif |
| |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) |
| #define IWE_STREAM_ADD_VALUE(p1, p2, p3, p4, p5, p6) \ |
| iwe_stream_add_value((p1), (p2), (p3), (p4), (p5), (p6)) |
| #else |
| #define IWE_STREAM_ADD_VALUE(p1, p2, p3, p4, p5, p6) \ |
| iwe_stream_add_value((p2), (p3), (p4), (p5), (p6)) |
| #endif |
| |
| static void ar6000_set_quality(struct iw_quality *iq, A_INT8 rssi); |
| extern unsigned int wmitimeout; |
| #if WLAN_CONFIG_FIRST_SCAN_2G_ONLY |
| extern unsigned int first_scan_2g_only; |
| #endif |
| extern unsigned int psm_info; |
| |
| #if WIRELESS_EXT > 14 |
| /* |
| * 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 += snprintf((char*)p, bufsize, "%02x", ie[i]); |
| bufsize -= 2; |
| } |
| return (i == ielen ? p - (u_int8_t *)buf : 0); |
| } |
| #endif /* WIRELESS_EXT > 14 */ |
| |
| static A_UINT8 |
| get_bss_phy_capability(bss_t *bss) |
| { |
| A_UINT8 capability = 0; |
| struct ieee80211_common_ie *cie = &bss->ni_cie; |
| #define CHAN_IS_11A(x) (!((x >= 2412) && (x <= 2484))) |
| if (CHAN_IS_11A(cie->ie_chan)) { |
| if (cie->ie_htcap) { |
| capability = WMI_11NA_CAPABILITY; |
| } else { |
| capability = WMI_11A_CAPABILITY; |
| } |
| } else if ((cie->ie_erp) || (cie->ie_xrates)) { |
| if (cie->ie_htcap) { |
| capability = WMI_11NG_CAPABILITY; |
| } else { |
| capability = WMI_11G_CAPABILITY; |
| } |
| } |
| return capability; |
| } |
| |
| void |
| ar6000_scan_node(void *arg, bss_t *ni) |
| { |
| struct iw_event iwe; |
| #if WIRELESS_EXT > 14 |
| char buf[256]; |
| #endif |
| struct ar_giwscan_param *param; |
| A_CHAR *current_ev; |
| A_CHAR *end_buf; |
| struct ieee80211_common_ie *cie; |
| A_CHAR *current_val; |
| A_INT32 j; |
| A_UINT32 rate_len, data_len = 0; |
| |
| /* Node table now contains entries from P2P Action frames and Probe Request also. Return |
| * if the frame type is an action frame/ Probe request. |
| */ |
| if ((ni->ni_frametype == ACTION_MGMT_FTYPE) || (ni->ni_frametype == PROBEREQ_FTYPE) || (ni->ni_buf == NULL)) { |
| return; |
| } |
| |
| param = (struct ar_giwscan_param *)arg; |
| |
| current_ev = param->current_ev; |
| end_buf = param->end_buf; |
| |
| cie = &ni->ni_cie; |
| if (cie == NULL) { |
| return; |
| } |
| |
| if ((end_buf - current_ev) > IW_EV_ADDR_LEN) |
| { |
| 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(param->info, current_ev, end_buf, |
| &iwe, IW_EV_ADDR_LEN); |
| } |
| param->bytes_needed += IW_EV_ADDR_LEN; |
| |
| /* EV92904 cie->ie_ssid can be NULL */ |
| if (cie->ie_ssid) { |
| data_len = cie->ie_ssid[1] + IW_EV_POINT_LEN; |
| } else { |
| data_len = IW_EV_POINT_LEN; |
| } |
| |
| if ((end_buf - current_ev) > data_len) |
| { |
| A_MEMZERO(&iwe, sizeof(iwe)); |
| iwe.cmd = SIOCGIWESSID; |
| iwe.u.data.flags = 1; |
| if (cie->ie_ssid) { |
| iwe.u.data.length = cie->ie_ssid[1]; |
| current_ev = IWE_STREAM_ADD_POINT(param->info, current_ev, end_buf, |
| &iwe, (char*)&cie->ie_ssid[2]); |
| } else { |
| iwe.u.data.length = 0; |
| current_ev = IWE_STREAM_ADD_POINT(param->info, current_ev, end_buf, |
| &iwe, (char*)""); |
| } |
| } |
| param->bytes_needed += data_len; |
| |
| if (cie->ie_capInfo & (IEEE80211_CAPINFO_ESS|IEEE80211_CAPINFO_IBSS)) { |
| if ((end_buf - current_ev) > IW_EV_UINT_LEN) |
| { |
| 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(param->info, current_ev, end_buf, |
| &iwe, IW_EV_UINT_LEN); |
| } |
| param->bytes_needed += IW_EV_UINT_LEN; |
| } |
| |
| if ((end_buf - current_ev) > IW_EV_FREQ_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(param->info, current_ev, end_buf, |
| &iwe, IW_EV_FREQ_LEN); |
| } |
| param->bytes_needed += IW_EV_FREQ_LEN; |
| |
| if ((end_buf - current_ev) > IW_EV_QUAL_LEN) |
| { |
| A_MEMZERO(&iwe, sizeof(iwe)); |
| iwe.cmd = IWEVQUAL; |
| ar6000_set_quality(&iwe.u.qual, ni->ni_snr); |
| current_ev = IWE_STREAM_ADD_EVENT(param->info, current_ev, end_buf, |
| &iwe, IW_EV_QUAL_LEN); |
| } |
| param->bytes_needed += IW_EV_QUAL_LEN; |
| |
| if ((end_buf - current_ev) > IW_EV_POINT_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(param->info, current_ev, end_buf, |
| &iwe, ""); |
| } |
| param->bytes_needed += IW_EV_POINT_LEN; |
| |
| /* supported bit rate */ |
| if ((cie->ie_rates != NULL) || (cie->ie_xrates != NULL)) { |
| A_MEMZERO(&iwe, sizeof(iwe)); |
| iwe.cmd = SIOCGIWRATE; |
| iwe.u.bitrate.fixed = 0; |
| iwe.u.bitrate.disabled = 0; |
| iwe.u.bitrate.value = 0; |
| current_val = current_ev + IW_EV_LCP_LEN; |
| param->bytes_needed += IW_EV_LCP_LEN; |
| |
| if (cie->ie_rates != NULL) { |
| rate_len = cie->ie_rates[1]; |
| data_len = (rate_len * (IW_EV_PARAM_LEN - IW_EV_LCP_LEN)); |
| if ((end_buf - current_ev) > data_len) |
| { |
| for (j = 0; j < rate_len; j++) { |
| unsigned char val; |
| val = cie->ie_rates[2 + j]; |
| iwe.u.bitrate.value = |
| (val >= 0x80)? ((val - 0x80) * 500000): (val * 500000); |
| current_val = IWE_STREAM_ADD_VALUE(param->info, current_ev, |
| current_val, end_buf, |
| &iwe, IW_EV_PARAM_LEN); |
| } |
| } |
| param->bytes_needed += data_len; |
| } |
| |
| if (cie->ie_xrates != NULL) { |
| rate_len = cie->ie_xrates[1]; |
| data_len = (rate_len * (IW_EV_PARAM_LEN - IW_EV_LCP_LEN)); |
| if ((end_buf - current_ev) > data_len) |
| { |
| for (j = 0; j < rate_len; j++) { |
| unsigned char val; |
| val = cie->ie_xrates[2 + j]; |
| iwe.u.bitrate.value = |
| (val >= 0x80)? ((val - 0x80) * 500000): (val * 500000); |
| current_val = IWE_STREAM_ADD_VALUE(param->info, current_ev, |
| current_val, end_buf, |
| &iwe, IW_EV_PARAM_LEN); |
| } |
| } |
| param->bytes_needed += data_len; |
| } |
| /* remove fixed header if no rates were added */ |
| if ((current_val - current_ev) > IW_EV_LCP_LEN) |
| current_ev = current_val; |
| } |
| |
| #if WIRELESS_EXT >= 18 |
| /* IE */ |
| if (cie->ie_wpa != NULL) { |
| data_len = cie->ie_wpa[1] + 2 + IW_EV_POINT_LEN; |
| if ((end_buf - current_ev) > data_len) |
| { |
| A_MEMZERO(&iwe, sizeof(iwe)); |
| iwe.cmd = IWEVGENIE; |
| iwe.u.data.length = cie->ie_wpa[1] + 2; |
| current_ev = IWE_STREAM_ADD_POINT(param->info, current_ev, end_buf, |
| &iwe, (char*)cie->ie_wpa); |
| } |
| param->bytes_needed += data_len; |
| } |
| |
| if (cie->ie_rsn != NULL && cie->ie_rsn[0] == IEEE80211_ELEMID_RSN) { |
| data_len = cie->ie_rsn[1] + 2 + IW_EV_POINT_LEN; |
| if ((end_buf - current_ev) > data_len) |
| { |
| A_MEMZERO(&iwe, sizeof(iwe)); |
| iwe.cmd = IWEVGENIE; |
| iwe.u.data.length = cie->ie_rsn[1] + 2; |
| current_ev = IWE_STREAM_ADD_POINT(param->info, current_ev, end_buf, |
| &iwe, (char*)cie->ie_rsn); |
| } |
| param->bytes_needed += data_len; |
| } |
| |
| #endif /* WIRELESS_EXT >= 18 */ |
| |
| if ((end_buf - current_ev) > IW_EV_CHAR_LEN) |
| { |
| /* protocol */ |
| A_MEMZERO(&iwe, sizeof(iwe)); |
| iwe.cmd = SIOCGIWNAME; |
| switch (get_bss_phy_capability(ni)) { |
| case WMI_11A_CAPABILITY: |
| snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11a"); |
| break; |
| case WMI_11G_CAPABILITY: |
| snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11g"); |
| break; |
| case WMI_11NA_CAPABILITY: |
| snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11na"); |
| break; |
| case WMI_11NG_CAPABILITY: |
| snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11ng"); |
| break; |
| default: |
| snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11b"); |
| break; |
| } |
| current_ev = IWE_STREAM_ADD_EVENT(param->info, current_ev, end_buf, |
| &iwe, IW_EV_CHAR_LEN); |
| } |
| param->bytes_needed += IW_EV_CHAR_LEN; |
| |
| #if WIRELESS_EXT > 14 |
| A_MEMZERO(&iwe, sizeof(iwe)); |
| iwe.cmd = IWEVCUSTOM; |
| iwe.u.data.length = snprintf(buf, sizeof(buf), "bcn_int=%d", cie->ie_beaconInt); |
| data_len = iwe.u.data.length + IW_EV_POINT_LEN; |
| if ((end_buf - current_ev) > data_len) |
| { |
| current_ev = IWE_STREAM_ADD_POINT(param->info, current_ev, end_buf, |
| &iwe, buf); |
| } |
| param->bytes_needed += data_len; |
| |
| #if WIRELESS_EXT < 18 |
| if (cie->ie_wpa != NULL) { |
| static const char wpa_leader[] = "wpa_ie="; |
| data_len = (sizeof(wpa_leader) - 1) + ((cie->ie_wpa[1]+2) * 2) + IW_EV_POINT_LEN; |
| if ((end_buf - current_ev) > data_len) |
| { |
| 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(param->info, current_ev, |
| end_buf, &iwe, buf); |
| } |
| } |
| param->bytes_needed += data_len; |
| } |
| |
| if (cie->ie_rsn != NULL && cie->ie_rsn[0] == IEEE80211_ELEMID_RSN) { |
| static const char rsn_leader[] = "rsn_ie="; |
| data_len = (sizeof(rsn_leader) - 1) + ((cie->ie_rsn[1]+2) * 2) + IW_EV_POINT_LEN; |
| if ((end_buf - current_ev) > data_len) |
| { |
| 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(param->info, current_ev, |
| end_buf, &iwe, buf); |
| } |
| } |
| param->bytes_needed += data_len; |
| } |
| #endif /* WIRELESS_EXT < 18 */ |
| |
| if (cie->ie_wmm != NULL) { |
| static const char wmm_leader[] = "wmm_ie="; |
| data_len = (sizeof(wmm_leader) - 1) + ((cie->ie_wmm[1]+2) * 2) + IW_EV_POINT_LEN; |
| if ((end_buf - current_ev) > data_len) |
| { |
| 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(param->info, current_ev, |
| end_buf, &iwe, buf); |
| } |
| } |
| param->bytes_needed += data_len; |
| } |
| |
| if (cie->ie_ath != NULL) { |
| static const char ath_leader[] = "ath_ie="; |
| data_len = (sizeof(ath_leader) - 1) + ((cie->ie_ath[1]+2) * 2) + IW_EV_POINT_LEN; |
| if ((end_buf - current_ev) > data_len) |
| { |
| 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(param->info, current_ev, |
| end_buf, &iwe, buf); |
| } |
| } |
| param->bytes_needed += data_len; |
| } |
| |
| #ifdef WAPI_ENABLE |
| if (cie->ie_wapi != NULL) { |
| static const char wapi_leader[] = "wapi_ie="; |
| data_len = (sizeof(wapi_leader) - 1) + ((cie->ie_wapi[1] + 2) * 2) + IW_EV_POINT_LEN; |
| if ((end_buf - current_ev) > data_len) { |
| A_MEMZERO(&iwe, sizeof(iwe)); |
| iwe.cmd = IWEVCUSTOM; |
| iwe.u.data.length = encode_ie(buf, sizeof(buf), cie->ie_wapi, |
| cie->ie_wapi[1] + 2, |
| wapi_leader, sizeof(wapi_leader) - 1); |
| if (iwe.u.data.length != 0) { |
| current_ev = IWE_STREAM_ADD_POINT(param->info, current_ev, |
| end_buf, &iwe, buf); |
| } |
| } |
| param->bytes_needed += data_len; |
| } |
| #endif /* WAPI_ENABLE */ |
| |
| #ifdef HS20_ENABLE |
| if (cie->ie_hs20 != NULL) { |
| static const char hs20_leader[] = "hs20_ie="; |
| data_len = (sizeof(hs20_leader) - 1) + ((cie->ie_hs20[1] + 2) * 2) + IW_EV_POINT_LEN; |
| if ((end_buf - current_ev) > data_len) { |
| A_MEMZERO(&iwe, sizeof(iwe)); |
| iwe.cmd = IWEVCUSTOM; |
| iwe.u.data.length = encode_ie(buf, sizeof(buf), cie->ie_hs20, |
| cie->ie_hs20[1] + 2, |
| hs20_leader, sizeof(hs20_leader) - 1); |
| if (iwe.u.data.length != 0) { |
| current_ev = IWE_STREAM_ADD_POINT(param->info, current_ev, |
| end_buf, &iwe, buf); |
| } |
| } |
| param->bytes_needed += data_len; |
| } |
| if (cie->ie_extcap != NULL) { |
| static const char extcap_leader[] = "extcap_ie="; |
| data_len = (sizeof(extcap_leader) - 1) + ((cie->ie_extcap[1] + 2) * 2) + IW_EV_POINT_LEN; |
| if ((end_buf - current_ev) > data_len) { |
| A_MEMZERO(&iwe, sizeof(iwe)); |
| iwe.cmd = IWEVCUSTOM; |
| iwe.u.data.length = encode_ie(buf, sizeof(buf), cie->ie_extcap, |
| cie->ie_extcap[1] + 2, |
| extcap_leader, sizeof(extcap_leader) - 1); |
| if (iwe.u.data.length != 0) { |
| current_ev = IWE_STREAM_ADD_POINT(param->info, current_ev, |
| end_buf, &iwe, buf); |
| } |
| } |
| param->bytes_needed += data_len; |
| } |
| #endif |
| #endif /* WIRELESS_EXT > 14 */ |
| |
| #if WIRELESS_EXT >= 18 |
| if (cie->ie_wsc != NULL) { |
| data_len = (cie->ie_wsc[1] + 2) + IW_EV_POINT_LEN; |
| if ((end_buf - current_ev) > data_len) |
| { |
| A_MEMZERO(&iwe, sizeof(iwe)); |
| iwe.cmd = IWEVGENIE; |
| iwe.u.data.length = cie->ie_wsc[1] + 2; |
| current_ev = IWE_STREAM_ADD_POINT(param->info, current_ev, end_buf, |
| &iwe, (char*)cie->ie_wsc); |
| } |
| param->bytes_needed += data_len; |
| } |
| #endif /* WIRELESS_EXT >= 18 */ |
| |
| 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_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| struct ar_giwscan_param param; |
| |
| if (is_iwioctl_allowed(arPriv->arNextMode, info->cmd) != A_OK) { |
| AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("wext_ioctl: cmd=0x%x not allowed in this mode\n", info->cmd)); |
| return -EOPNOTSUPP; |
| } |
| |
| if (ar->arWlanState == WLAN_DISABLED) { |
| return -EIO; |
| } |
| |
| if (ar->arWmiReady == FALSE) { |
| return -EIO; |
| } |
| |
| if (down_interruptible(&ar->arSem)) { |
| return -ERESTARTSYS; |
| } |
| |
| if (ar->bIsDestroyProgress || ar->arWlanState == WLAN_DISABLED) { |
| up(&ar->arSem); |
| return -EBUSY; |
| } |
| |
| param.current_ev = extra; |
| param.end_buf = extra + data->length; |
| param.bytes_needed = 0; |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) |
| param.info = info; |
| #endif |
| |
| wmi_scan_report_lock(arPriv->arWmi); |
| |
| /* Translate data to WE format */ |
| wmi_iterate_nodes(arPriv->arWmi, ar6000_scan_node, ¶m); |
| |
| wmi_scan_report_unlock(arPriv->arWmi); |
| |
| /* check if bytes needed is greater than bytes consumed */ |
| if (data->length == 65535) { |
| param.bytes_needed = param.current_ev - extra; |
| } else { |
| if (param.bytes_needed > data->length) |
| { |
| /* Request one byte more than needed, because when "data->length" equals bytes_needed, |
| it is not possible to add the last event data as all iwe_stream_add_xxxxx() functions |
| checks whether (cur_ptr + ev_len) < end_ptr, due to this one more retry would happen*/ |
| data->length = param.bytes_needed + 1; |
| |
| up(&ar->arSem); |
| return -E2BIG; |
| } |
| } |
| |
| up(&ar->arSem); |
| 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_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| AR_SOFTC_DEV_T *arTempPriv = NULL; |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| AR_SOFTC_STA_T *arSta = &arPriv->arSta; |
| A_STATUS status; |
| A_UINT8 i=0; |
| |
| if (is_iwioctl_allowed(arPriv->arNextMode, info->cmd) != A_OK) { |
| AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("wext_ioctl: cmd=0x%x not allowed in this mode\n", info->cmd)); |
| return -EOPNOTSUPP; |
| } |
| |
| if (ar->bIsDestroyProgress) { |
| return -EBUSY; |
| } |
| |
| if (ar->arWlanState == WLAN_DISABLED) { |
| return -EIO; |
| } |
| |
| if (ar->arWmiReady == FALSE) { |
| return -EIO; |
| } |
| |
| if (down_interruptible(&ar->arSem)) { |
| return -ERESTARTSYS; |
| } |
| |
| if (ar->bIsDestroyProgress || ar->arWlanState == WLAN_DISABLED) { |
| up(&ar->arSem); |
| return -EBUSY; |
| } |
| |
| |
| #if defined(WIRELESS_EXT) |
| if (WIRELESS_EXT > 20) { |
| data->length += 1; |
| } |
| #endif |
| /* Handling AP-STA Concurrency */ |
| if((ar->arConfNumDev > 1)) { |
| if ((data->flags) && |
| (arPriv->arNetworkType == INFRA_NETWORK) && |
| ((arPriv->arSsidLen != (data->length - 1)) || |
| (A_MEMCMP(arPriv->arSsid, ssid, arPriv->arSsidLen) != 0))){ |
| for(i=0;i<ar->arConfNumDev;i++) { |
| arTempPriv = ar->arDev[i]; |
| if((AP_NETWORK == arTempPriv->arNetworkType) && |
| (arTempPriv->arConnected) && |
| (arTempPriv->arNetworkSubType != SUBTYPE_NONE)) { |
| ar6000_disconnect(arPriv); |
| /*Disconnect AP only in P2P mode*/ |
| } |
| } |
| } |
| } |
| /* |
| * iwconfig passes a null terminated string with length including this |
| * so we need to account for this |
| */ |
| if (data->flags && (!data->length || (data->length == 1) || |
| ((data->length - 1) > sizeof(arPriv->arSsid)))) |
| { |
| /* |
| * ssid is invalid |
| */ |
| up(&ar->arSem); |
| return -EINVAL; |
| } |
| |
| if (arPriv->arNetworkType == AP_NETWORK) { |
| if(!data->flags) { |
| /* stop AP */ |
| ar6000_disconnect(arPriv); |
| if(arPriv->arNetworkSubType == SUBTYPE_P2PGO) { |
| wait_event_interruptible_timeout(arPriv->arEvent, arPriv->arConnected == FALSE, wmitimeout * HZ); |
| if (signal_pending(current)) { |
| return -EINTR; |
| } |
| } |
| |
| } else if(A_MEMCMP(arPriv->arSsid,ssid,32) != 0) { |
| /* SSID change for AP network - Will take effect on commit */ |
| arPriv->arSsidLen = data->length - 1; |
| A_MEMZERO(arPriv->arSsid, WMI_MAX_SSID_LEN); |
| A_MEMCPY(arPriv->arSsid, ssid, arPriv->arSsidLen); |
| } |
| arPriv->ap_profile_flag = 1; /* There is a change in profile */ |
| arPriv->arConnected = FALSE; |
| up(&ar->arSem); |
| return 0; |
| } |
| |
| /* 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 (arSta->arSkipScan == TRUE && |
| (arPriv->arChannelHint == 0 || |
| (!arSta->arReqBssid[0] && !arSta->arReqBssid[1] && !arSta->arReqBssid[2] && |
| !arSta->arReqBssid[3] && !arSta->arReqBssid[4] && !arSta->arReqBssid[5]))) |
| { |
| up(&ar->arSem); |
| return -EINVAL; |
| } |
| } |
| |
| |
| if (ar->arTxPending[wmi_get_control_ep(arPriv->arWmi)]) { |
| /* |
| * sleep until the command queue drains |
| */ |
| wait_event_interruptible_timeout(arPriv->arEvent, |
| ar->arTxPending[wmi_get_control_ep(arPriv->arWmi)] == 0, wmitimeout * HZ); |
| if (signal_pending(current)) { |
| up(&ar->arSem); |
| return -EINTR; |
| } |
| } |
| |
| |
| if (!data->flags) { |
| #ifdef ATH6K_CONFIG_CFG80211 |
| if (arPriv->arConnected) { |
| #endif /* ATH6K_CONFIG_CFG80211 */ |
| ar6000_init_mode_info(arPriv); |
| #ifdef ATH6K_CONFIG_CFG80211 |
| } |
| #endif /* ATH6K_CONFIG_CFG80211 */ |
| } |
| |
| if (((arPriv->arSsidLen) || |
| ((arPriv->arSsidLen == 0) && (arPriv->arConnected || arSta->arConnectPending)) || |
| (!data->flags))) |
| { |
| if ((!data->flags) || |
| (A_MEMCMP(arPriv->arSsid, ssid, arPriv->arSsidLen) != 0) || |
| (arPriv->arSsidLen != (data->length - 1))) |
| { |
| /* |
| * 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_setPmkid_cmd(arPriv->arWmi, arPriv->arBssid, NULL, 0); |
| if(data->length) { |
| /*AR6000 intiated disconnect due to profile change*/ |
| arPriv->arSta.arHostDisconnect = 1; |
| } |
| ar6000_disconnect(arPriv); |
| A_MEMZERO(arPriv->arSsid, sizeof(arPriv->arSsid)); |
| arPriv->arSsidLen = 0; |
| if (arSta->arSkipScan == FALSE) { |
| A_MEMZERO(arSta->arReqBssid, sizeof(arSta->arReqBssid)); |
| } |
| if (!data->flags) { |
| up(&ar->arSem); |
| return 0; |
| } |
| } else { |
| up(&ar->arSem); |
| return -EIO; |
| } |
| } |
| 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((arPriv->arConnected == TRUE) && (ar->arWmiReady == TRUE)) |
| { |
| reconnect_flag = TRUE; |
| status = wmi_reconnect_cmd(arPriv->arWmi,arSta->arReqBssid, |
| arPriv->arChannelHint); |
| up(&ar->arSem); |
| if (status != A_OK) { |
| return -EIO; |
| } |
| return 0; |
| } |
| else{ |
| /* |
| * Dont return if connect is pending. |
| */ |
| if(!(arSta->arConnectPending)) { |
| up(&ar->arSem); |
| return 0; |
| } |
| } |
| } |
| } |
| |
| arPriv->arSsidLen = data->length - 1; |
| A_MEMCPY(arPriv->arSsid, ssid, arPriv->arSsidLen); |
| |
| if (ar6000_connect_to_ap(arPriv)!= A_OK) { |
| up(&ar->arSem); |
| return -EIO; |
| }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_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| |
| if (is_iwioctl_allowed(arPriv->arNextMode, info->cmd) != A_OK) { |
| AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("wext_ioctl: cmd=0x%x not allowed in this mode\n", info->cmd)); |
| return -EOPNOTSUPP; |
| } |
| |
| if (ar->arWlanState == WLAN_DISABLED) { |
| return -EIO; |
| } |
| |
| if (!arPriv->arSsidLen) { |
| return -EINVAL; |
| } |
| |
| data->flags = 1; |
| data->length = arPriv->arSsidLen; |
| A_MEMCPY(essid, arPriv->arSsid, arPriv->arSsidLen); |
| |
| return 0; |
| } |
| |
| |
| void ar6000_install_static_wep_keys(AR_SOFTC_DEV_T *arPriv) |
| { |
| A_UINT8 index; |
| A_UINT8 keyUsage; |
| AR_SOFTC_AP_T *ap = &arPriv->arAp; |
| |
| for (index = WMI_MIN_KEY_INDEX; index <= WMI_MAX_KEY_INDEX; index++) { |
| if (arPriv->arWepKeyList[index].arKeyLen) { |
| keyUsage = GROUP_USAGE; |
| if (index == arPriv->arDefTxKeyIndex) { |
| keyUsage |= TX_USAGE; |
| } |
| wmi_addKey_cmd(arPriv->arWmi, |
| index, |
| WEP_CRYPT, |
| keyUsage, |
| arPriv->arWepKeyList[index].arKeyLen, |
| NULL, |
| arPriv->arWepKeyList[index].arKey, KEY_OP_INIT_VAL, NULL, |
| NO_SYNC_WMIFLAG); |
| } |
| } |
| ap->deKeySet = FALSE; |
| } |
| |
| /* |
| * SIOCSIWRATE |
| */ |
| int |
| ar6000_ioctl_siwrate(struct net_device *dev, |
| struct iw_request_info *info, |
| struct iw_param *rrq, char *extra) |
| { |
| AR_SOFTC_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| |
| A_UINT32 kbps; |
| A_INT8 rate_idx; |
| |
| if (is_iwioctl_allowed(arPriv->arNextMode, info->cmd) != A_OK) { |
| AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("wext_ioctl: cmd=0x%x not allowed in this mode\n", info->cmd)); |
| return -EOPNOTSUPP; |
| } |
| |
| if (down_interruptible(&ar->arSem)) { |
| return -ERESTARTSYS; |
| } |
| |
| if (ar->bIsDestroyProgress || ar->arWlanState == WLAN_DISABLED) { |
| up(&ar->arSem); |
| return -EBUSY; |
| } |
| |
| 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(arPriv->arWmi, kbps, &rate_idx) != A_OK) |
| { |
| AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("BitRate is not Valid %d\n", kbps)); |
| up(&ar->arSem); |
| return -EINVAL; |
| } |
| arPriv->arBitRate = kbps; |
| if(ar->arWmiReady == TRUE) |
| { |
| if (wmi_set_bitrate_cmd(arPriv->arWmi, kbps, -1, -1) != A_OK) { |
| up(&ar->arSem); |
| return -EINVAL; |
| } |
| } |
| |
| up(&ar->arSem); |
| return 0; |
| } |
| |
| /* |
| * SIOCGIWRATE |
| */ |
| int |
| ar6000_ioctl_giwrate(struct net_device *dev, |
| struct iw_request_info *info, |
| struct iw_param *rrq, char *extra) |
| { |
| AR_SOFTC_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| |
| int ret = 0; |
| |
| if (is_iwioctl_allowed(arPriv->arNextMode, info->cmd) != A_OK) { |
| AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("wext_ioctl: cmd=0x%x not allowed in this mode\n", info->cmd)); |
| return -EOPNOTSUPP; |
| } |
| |
| if (ar->bIsDestroyProgress) { |
| return -EBUSY; |
| } |
| |
| if (ar->arWlanState == WLAN_DISABLED) { |
| return -EIO; |
| } |
| |
| if ((arPriv->arNextMode != AP_NETWORK && !arPriv->arConnected) || ar->arWmiReady == FALSE) { |
| rrq->value = 1000 * 1000; |
| return 0; |
| } |
| |
| if (arPriv->arBitRate!=-1 && arPriv->arBitRate!=0xFFFF) { |
| rrq->fixed = TRUE; |
| rrq->value = arPriv->arBitRate * 1000; |
| return 0; |
| } |
| |
| if (down_interruptible(&ar->arSem)) { |
| return -ERESTARTSYS; |
| } |
| |
| if (ar->bIsDestroyProgress || ar->arWlanState == WLAN_DISABLED) { |
| up(&ar->arSem); |
| return -EBUSY; |
| } |
| |
| arPriv->arBitRate = 0xFFFF; |
| if (wmi_get_bitrate_cmd(arPriv->arWmi) != A_OK) { |
| up(&ar->arSem); |
| return -EIO; |
| } |
| wait_event_interruptible_timeout(arPriv->arEvent, arPriv->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 (arPriv->arBitRate == -1) { |
| rrq->fixed = TRUE; |
| rrq->value = 0; |
| } else { |
| rrq->fixed = FALSE; |
| rrq->value = arPriv->arBitRate * 1000; |
| } |
| arPriv->arBitRate = -1; /* clean it up for next query */ |
| } |
| |
| 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_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| |
| A_UINT8 dbM; |
| |
| if (is_iwioctl_allowed(arPriv->arNextMode, info->cmd) != A_OK) { |
| AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("wext_ioctl: cmd=0x%x not allowed in this mode\n", info->cmd)); |
| return -EOPNOTSUPP; |
| } |
| |
| if (ar->arWlanState == WLAN_DISABLED) { |
| return -EIO; |
| } |
| |
| if (down_interruptible(&ar->arSem)) { |
| return -ERESTARTSYS; |
| } |
| |
| if (ar->bIsDestroyProgress || ar->arWlanState == WLAN_DISABLED) { |
| up(&ar->arSem); |
| return -EBUSY; |
| } |
| |
| if (rrq->disabled) { |
| up(&ar->arSem); |
| return -EOPNOTSUPP; |
| } |
| |
| if (rrq->fixed) { |
| if (rrq->flags != IW_TXPOW_DBM) { |
| up(&ar->arSem); |
| return -EOPNOTSUPP; |
| } |
| arPriv->arTxPwr= dbM = rrq->value; |
| arPriv->arTxPwrSet = TRUE; |
| } else { |
| arPriv->arTxPwr = dbM = 0; |
| arPriv->arTxPwrSet = FALSE; |
| } |
| if(ar->arWmiReady == TRUE) |
| { |
| AR_DEBUG_PRINTF(ATH_DEBUG_WLAN_TX,("Set tx pwr cmd %d dbM\n", dbM)); |
| wmi_set_txPwr_cmd(arPriv->arWmi, dbM); |
| } |
| |
| up(&ar->arSem); |
| return 0; |
| } |
| |
| /* |
| * SIOCGIWTXPOW |
| */ |
| int |
| ar6000_ioctl_giwtxpow(struct net_device *dev, |
| struct iw_request_info *info, |
| struct iw_param *rrq, char *extra) |
| { |
| AR_SOFTC_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| |
| int ret = 0; |
| |
| if (is_iwioctl_allowed(arPriv->arNextMode, info->cmd) != A_OK) { |
| AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("wext_ioctl: cmd=0x%x not allowed in this mode\n", info->cmd)); |
| return -EOPNOTSUPP; |
| } |
| |
| if (ar->bIsDestroyProgress) { |
| return -EBUSY; |
| } |
| |
| if (ar->arWlanState == WLAN_DISABLED) { |
| return -EIO; |
| } |
| |
| if (down_interruptible(&ar->arSem)) { |
| return -ERESTARTSYS; |
| } |
| |
| if (ar->bIsDestroyProgress || ar->arWlanState == WLAN_DISABLED) { |
| up(&ar->arSem); |
| return -EBUSY; |
| } |
| |
| if((ar->arWmiReady == TRUE) && (arPriv->arConnected == TRUE)) |
| { |
| arPriv->arTxPwr = 0; |
| |
| if (wmi_get_txPwr_cmd(arPriv->arWmi) != A_OK) { |
| up(&ar->arSem); |
| return -EIO; |
| } |
| |
| wait_event_interruptible_timeout(arPriv->arEvent, arPriv->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 (arPriv->arTxPwrSet == TRUE) { |
| rrq->fixed = TRUE; |
| } |
| rrq->value = arPriv->arTxPwr; |
| rrq->flags = IW_TXPOW_DBM; |
| // |
| // IWLIST need this flag to get TxPower |
| // |
| rrq->disabled = 0; |
| } |
| |
| 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_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| |
| if (is_iwioctl_allowed(arPriv->arNextMode, info->cmd) != A_OK) { |
| AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("wext_ioctl: cmd=0x%x not allowed in this mode\n", info->cmd)); |
| return -EOPNOTSUPP; |
| } |
| |
| if (ar->arWlanState == WLAN_DISABLED) { |
| return -EIO; |
| } |
| |
| if (down_interruptible(&ar->arSem)) { |
| return -ERESTARTSYS; |
| } |
| |
| if (ar->bIsDestroyProgress || ar->arWlanState == WLAN_DISABLED) { |
| up(&ar->arSem); |
| return -EBUSY; |
| } |
| |
| if (rrq->disabled) { |
| up(&ar->arSem); |
| return -EOPNOTSUPP; |
| } |
| |
| if ((rrq->flags & IW_RETRY_TYPE) != IW_RETRY_LIMIT) { |
| up(&ar->arSem); |
| return -EOPNOTSUPP; |
| } |
| |
| if ( !(rrq->value >= WMI_MIN_RETRIES) || !(rrq->value <= WMI_MAX_RETRIES)) { |
| up(&ar->arSem); |
| return - EINVAL; |
| } |
| if(ar->arWmiReady == TRUE) |
| { |
| if (wmi_set_retry_limits_cmd(arPriv->arWmi, DATA_FRAMETYPE, WMM_AC_BE, |
| rrq->value, 0) != A_OK){ |
| up(&ar->arSem); |
| return -EINVAL; |
| } |
| } |
| arPriv->arMaxRetries = rrq->value; |
| |
| up(&ar->arSem); |
| 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_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| |
| |
| if (is_iwioctl_allowed(arPriv->arNextMode, info->cmd) != A_OK) { |
| AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("wext_ioctl: cmd=0x%x not allowed in this mode\n", info->cmd)); |
| return -EOPNOTSUPP; |
| } |
| |
| 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 = arPriv->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_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| AR_SOFTC_AP_T *ap = &arPriv->arAp; |
| |
| int index; |
| A_INT32 auth = 0; |
| |
| if (is_iwioctl_allowed(arPriv->arNextMode, info->cmd) != A_OK) { |
| AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("wext_ioctl: cmd=0x%x not allowed in this mode\n", info->cmd)); |
| return -EOPNOTSUPP; |
| } |
| |
| if(arPriv->arNextMode != AP_NETWORK) { |
| /* |
| * Static WEP Keys should be configured before setting the SSID |
| */ |
| if (arPriv->arSsid[0] && erq->length) { |
| return -EIO; |
| } |
| } |
| |
| if (ar->arWlanState == WLAN_DISABLED) { |
| return -EIO; |
| } |
| |
| if (down_interruptible(&ar->arSem)) { |
| return -ERESTARTSYS; |
| } |
| |
| if (ar->bIsDestroyProgress || ar->arWlanState == WLAN_DISABLED) { |
| up(&ar->arSem); |
| return -EBUSY; |
| } |
| |
| index = erq->flags & IW_ENCODE_INDEX; |
| |
| if (index && (((index - 1) < WMI_MIN_KEY_INDEX) || |
| ((index - 1) > WMI_MAX_KEY_INDEX))) |
| { |
| up(&ar->arSem); |
| 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(arPriv->arWepKeyList[index].arKey, |
| sizeof(arPriv->arWepKeyList[index].arKey)); |
| arPriv->arWepKeyList[index].arKeyLen = 0; |
| } |
| arPriv->arDot11AuthMode = OPEN_AUTH; |
| arPriv->arPairwiseCrypto = NONE_CRYPT; |
| arPriv->arGroupCrypto = NONE_CRYPT; |
| arPriv->arAuthMode = WMI_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; |
| } |
| if (erq->flags & IW_ENCODE_RESTRICTED) { |
| auth |= SHARED_AUTH; |
| } |
| |
| if (!auth) { |
| auth = OPEN_AUTH; |
| } |
| |
| if (!ap->deKeySet) { |
| arPriv->arDefTxKeyIndex = index; |
| } |
| |
| if (erq->length) { |
| if (!IEEE80211_IS_VALID_WEP_CIPHER_LEN(erq->length)) { |
| up(&ar->arSem); |
| return -EIO; |
| } |
| |
| A_MEMZERO(arPriv->arWepKeyList[index].arKey, |
| sizeof(arPriv->arWepKeyList[index].arKey)); |
| A_MEMCPY(arPriv->arWepKeyList[index].arKey, keybuf, erq->length); |
| arPriv->arWepKeyList[index].arKeyLen = erq->length; |
| arPriv->arDot11AuthMode = auth; |
| } else { |
| if (arPriv->arWepKeyList[index].arKeyLen == 0) { |
| up(&ar->arSem); |
| return -EIO; |
| } |
| arPriv->arDefTxKeyIndex = index; |
| |
| if(arPriv->arSsidLen && arPriv->arWepKeyList[index].arKeyLen) { |
| wmi_addKey_cmd(arPriv->arWmi, |
| index, |
| WEP_CRYPT, |
| GROUP_USAGE | TX_USAGE, |
| arPriv->arWepKeyList[index].arKeyLen, |
| NULL, |
| arPriv->arWepKeyList[index].arKey, KEY_OP_INIT_VAL, NULL, |
| NO_SYNC_WMIFLAG); |
| } |
| ap->deKeySet = TRUE; |
| } |
| |
| arPriv->arPairwiseCrypto = WEP_CRYPT; |
| arPriv->arGroupCrypto = WEP_CRYPT; |
| arPriv->arAuthMode = WMI_NONE_AUTH; |
| } |
| |
| if(arPriv->arNextMode != AP_NETWORK) { |
| /* |
| * profile has changed. Erase ssid to signal change |
| */ |
| A_MEMZERO(arPriv->arSsid, sizeof(arPriv->arSsid)); |
| arPriv->arSsidLen = 0; |
| } |
| arPriv->ap_profile_flag = 1; /* There is a change in profile */ |
| up(&ar->arSem); |
| return 0; |
| } |
| |
| static int |
| ar6000_ioctl_giwencode(struct net_device *dev, |
| struct iw_request_info *info, |
| struct iw_point *erq, char *key) |
| { |
| AR_SOFTC_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| |
| A_UINT8 keyIndex; |
| struct ar_wep_key *wk; |
| |
| if (is_iwioctl_allowed(arPriv->arNextMode, info->cmd) != A_OK) { |
| AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("wext_ioctl: cmd=0x%x not allowed in this mode\n", info->cmd)); |
| return -EOPNOTSUPP; |
| } |
| |
| if (ar->arWlanState == WLAN_DISABLED) { |
| return -EIO; |
| } |
| |
| if (down_interruptible(&ar->arSem)) { |
| return -ERESTARTSYS; |
| } |
| |
| if (ar->bIsDestroyProgress || ar->arWlanState == WLAN_DISABLED) { |
| up(&ar->arSem); |
| return -EBUSY; |
| } |
| |
| |
| if (arPriv->arPairwiseCrypto == NONE_CRYPT) { |
| erq->length = 0; |
| erq->flags = IW_ENCODE_DISABLED; |
| } else { |
| if (arPriv->arPairwiseCrypto == WEP_CRYPT) { |
| /* get the keyIndex */ |
| keyIndex = erq->flags & IW_ENCODE_INDEX; |
| if (0 == keyIndex) { |
| keyIndex = arPriv->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_DISABLED; |
| wk = &arPriv->arWepKeyList[keyIndex]; |
| if (erq->length > wk->arKeyLen) { |
| erq->length = wk->arKeyLen; |
| } |
| if (wk->arKeyLen) { |
| A_MEMCPY(key, wk->arKey, erq->length); |
| } |
| } else { |
| erq->flags &= ~IW_ENCODE_DISABLED; |
| if (arPriv->arSta.user_saved_keys.keyOk) { |
| erq->length = arPriv->arSta.user_saved_keys.ucast_ik.ik_keylen; |
| if (erq->length) { |
| A_MEMCPY(key, arPriv->arSta.user_saved_keys.ucast_ik.ik_keydata, erq->length); |
| } |
| } else { |
| erq->length = 1; // not really printing any key but let iwconfig know enc is on |
| } |
| } |
| |
| if (arPriv->arDot11AuthMode & OPEN_AUTH) { |
| erq->flags |= IW_ENCODE_OPEN; |
| } |
| if (arPriv->arDot11AuthMode & SHARED_AUTH) { |
| erq->flags |= IW_ENCODE_RESTRICTED; |
| } |
| } |
| |
| up(&ar->arSem); |
| return 0; |
| } |
| |
| #if WIRELESS_EXT >= 18 |
| static bool is_wpa_ie(const u8 *pos) |
| { |
| return pos[0] == IEEE80211_ELEMID_VENDOR && pos[1] >= 4 && |
| pos[2] == 0x00 && pos[3] == 0x50 && |
| pos[4] == 0xf2 && pos[5] == 0x01; |
| } |
| |
| static bool is_rsn_ie(const u8 *pos) |
| { |
| return pos[0] == IEEE80211_ELEMID_RSN; |
| } |
| |
| static bool is_wps_ie(const u8 *pos) |
| { |
| return (pos[0] == IEEE80211_ELEMID_VENDOR && |
| pos[1] >= 4 && |
| pos[2] == 0x00 && pos[3] == 0x50 && pos[4] == 0xf2 && |
| pos[5] == 0x04); |
| } |
| /* |
| * SIOCSIWGENIE |
| */ |
| static int |
| ar6000_ioctl_siwgenie(struct net_device *dev, |
| struct iw_request_info *info, |
| struct iw_point *erq, char *extra) |
| { |
| AR_SOFTC_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| A_UCHAR *ie = extra; |
| A_UINT16 ieLen = erq->length; |
| const A_UCHAR *pos; |
| A_UCHAR *buf = NULL; |
| int len = 0; |
| |
| if (ar->arWmiReady == FALSE || ar->arWlanState == WLAN_DISABLED) { |
| return -EIO; |
| } |
| if (ieLen > IEEE80211_APPIE_FRAME_MAX_LEN) { |
| return -EIO; |
| } |
| |
| if (down_interruptible(&ar->arSem)) { |
| return -ERESTARTSYS; |
| } |
| |
| if (ar->bIsDestroyProgress || ar->arWlanState == WLAN_DISABLED) { |
| up(&ar->arSem); |
| return -EBUSY; |
| } |
| |
| arPriv->arSta.arConnectCtrlFlags &= ~CONNECT_WPS_FLAG; |
| |
| if (ie && ieLen) { |
| buf = kmalloc(ieLen, GFP_KERNEL); |
| if (buf == NULL){ |
| up(&ar->arSem); |
| return -ENOMEM; |
| } |
| pos = ie; |
| |
| while (pos + 1 < ie + ieLen) { |
| if (pos + 2 + pos[1] > ie + ieLen) |
| break; |
| if (!(is_wpa_ie(pos) || is_rsn_ie(pos))) { |
| memcpy(buf + len, pos, 2 + pos[1]); |
| len += 2 + pos[1]; |
| } |
| |
| if (is_wps_ie(pos)){ |
| /* WPS IE detected, notify target */ |
| AR_DEBUG_PRINTF(ATH_DEBUG_INFO,("WPS IE detected -- setting WPS flag\n")); |
| arPriv->arSta.arConnectCtrlFlags |= CONNECT_WPS_FLAG; |
| } |
| |
| pos += 2 + pos[1]; |
| } |
| } |
| |
| wmi_set_appie_cmd(arPriv->arWmi, WMI_FRAME_ASSOC_REQ, len, buf); |
| kfree(buf); |
| up(&ar->arSem); |
| return 0; |
| } |
| |
| |
| |
| /* |
| * SIOCGIWGENIE |
| */ |
| static int |
| ar6000_ioctl_giwgenie(struct net_device *dev, |
| struct iw_request_info *info, |
| struct iw_point *erq, char *extra) |
| { |
| AR_SOFTC_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| |
| |
| if (ar->arWmiReady == FALSE) { |
| return -EIO; |
| } |
| erq->length = 0; |
| erq->flags = 0; |
| |
| return 0; |
| } |
| |
| /* |
| * SIOCSIWAUTH |
| */ |
| static int |
| ar6000_ioctl_siwauth(struct net_device *dev, |
| struct iw_request_info *info, |
| struct iw_param *data, char *extra) |
| { |
| AR_SOFTC_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| |
| |
| A_BOOL profChanged; |
| A_UINT16 param; |
| A_INT32 ret; |
| A_INT32 value; |
| |
| if (ar->arWmiReady == FALSE) { |
| return -EIO; |
| } |
| |
| if (ar->arWlanState == WLAN_DISABLED) { |
| return -EIO; |
| } |
| |
| |
| if (down_interruptible(&ar->arSem)) { |
| return -ERESTARTSYS; |
| } |
| |
| if (ar->bIsDestroyProgress || ar->arWlanState == WLAN_DISABLED) { |
| up(&ar->arSem); |
| return -EBUSY; |
| } |
| |
| param = data->flags & IW_AUTH_INDEX; |
| value = data->value; |
| profChanged = TRUE; |
| ret = 0; |
| |
| switch (param) { |
| case IW_AUTH_WPA_VERSION: |
| if (value & IW_AUTH_WPA_VERSION_DISABLED) { |
| arPriv->arAuthMode = WMI_NONE_AUTH; |
| } else if (value & IW_AUTH_WPA_VERSION_WPA) { |
| arPriv->arAuthMode = WMI_WPA_AUTH; |
| } else if (value & IW_AUTH_WPA_VERSION_WPA2) { |
| arPriv->arAuthMode = WMI_WPA2_AUTH; |
| } else { |
| ret = -1; |
| profChanged = FALSE; |
| } |
| if(!ret) { |
| if(arPriv->PreAuthMode == arPriv->arAuthMode) { |
| profChanged = FALSE; |
| } |
| arPriv->PreAuthMode = arPriv->arAuthMode; |
| } |
| break; |
| case IW_AUTH_CIPHER_PAIRWISE: |
| if (value & IW_AUTH_CIPHER_NONE) { |
| if(arPriv->arPairwiseCrypto == NONE_CRYPT) { |
| profChanged = FALSE; |
| } |
| arPriv->arPairwiseCrypto = NONE_CRYPT; |
| arPriv->arPairwiseCryptoLen = 0; |
| } else if (value & IW_AUTH_CIPHER_WEP40) { |
| if(arPriv->arPairwiseCrypto == WEP_CRYPT) { |
| profChanged = FALSE; |
| } |
| arPriv->arPairwiseCrypto = WEP_CRYPT; |
| arPriv->arPairwiseCryptoLen = 5; |
| } else if (value & IW_AUTH_CIPHER_TKIP) { |
| if(arPriv->arPairwiseCrypto == TKIP_CRYPT) { |
| profChanged = FALSE; |
| } |
| arPriv->arPairwiseCrypto = TKIP_CRYPT; |
| arPriv->arPairwiseCryptoLen = 0; |
| } else if (value & IW_AUTH_CIPHER_CCMP) { |
| if(arPriv->arPairwiseCrypto == AES_CRYPT) { |
| profChanged = FALSE; |
| } |
| arPriv->arPairwiseCrypto = AES_CRYPT; |
| arPriv->arPairwiseCryptoLen = 0; |
| } else if (value & IW_AUTH_CIPHER_WEP104) { |
| if(arPriv->arPairwiseCrypto == WEP_CRYPT) { |
| profChanged = FALSE; |
| } |
| arPriv->arPairwiseCrypto = WEP_CRYPT; |
| arPriv->arPairwiseCryptoLen = 13; |
| #ifdef WAPI_ENABLE |
| } else if (value & IW_AUTH_CIPHER_SMS4) { |
| if(arPriv->arPairwiseCrypto == WAPI_CRYPT) { |
| profChanged = FALSE; |
| } |
| arPriv->arPairwiseCrypto = WAPI_CRYPT; |
| arPriv->arPairwiseCryptoLen = 0; |
| #endif |
| } else { |
| ret = -1; |
| profChanged = FALSE; |
| } |
| break; |
| case IW_AUTH_CIPHER_GROUP: |
| if (value & IW_AUTH_CIPHER_NONE) { |
| if(arPriv->arGroupCrypto == NONE_CRYPT) { |
| profChanged = FALSE; |
| } |
| arPriv->arGroupCrypto = NONE_CRYPT; |
| arPriv->arGroupCryptoLen = 0; |
| } else if (value & IW_AUTH_CIPHER_WEP40) { |
| if(arPriv->arGroupCrypto == WEP_CRYPT) { |
| profChanged = FALSE; |
| } |
| arPriv->arGroupCrypto = WEP_CRYPT; |
| arPriv->arGroupCryptoLen = 5; |
| } else if (value & IW_AUTH_CIPHER_TKIP) { |
| if(arPriv->arGroupCrypto == TKIP_CRYPT) { |
| profChanged = FALSE; |
| } |
| arPriv->arGroupCrypto = TKIP_CRYPT; |
| arPriv->arGroupCryptoLen = 0; |
| } else if (value & IW_AUTH_CIPHER_CCMP) { |
| if(arPriv->arGroupCrypto == AES_CRYPT) { |
| profChanged = FALSE; |
| } |
| arPriv->arGroupCrypto = AES_CRYPT; |
| arPriv->arGroupCryptoLen = 0; |
| } else if (value & IW_AUTH_CIPHER_WEP104) { |
| if(arPriv->arGroupCrypto == WEP_CRYPT) { |
| profChanged = FALSE; |
| } |
| arPriv->arGroupCrypto = WEP_CRYPT; |
| arPriv->arGroupCryptoLen = 13; |
| #ifdef WAPI_ENABLE |
| } else if (value & IW_AUTH_CIPHER_SMS4) { |
| if(arPriv->arGroupCrypto == WAPI_CRYPT) { |
| profChanged = FALSE; |
| } |
| arPriv->arGroupCrypto = WAPI_CRYPT; |
| arPriv->arGroupCryptoLen = 0; |
| #endif |
| } else { |
| ret = -1; |
| profChanged = FALSE; |
| } |
| break; |
| case IW_AUTH_KEY_MGMT: |
| if (value & IW_AUTH_KEY_MGMT_PSK) { |
| if (WMI_WPA_AUTH == arPriv->arAuthMode) { |
| arPriv->arAuthMode = WMI_WPA_PSK_AUTH; |
| } else if (WMI_WPA2_AUTH == arPriv->arAuthMode) { |
| arPriv->arAuthMode = WMI_WPA2_PSK_AUTH; |
| } else { |
| ret = -1; |
| } |
| #define IW_AUTH_KEY_MGMT_CCKM 8 |
| } else if (value & IW_AUTH_KEY_MGMT_CCKM) { |
| if (WMI_WPA_AUTH == arPriv->arAuthMode) { |
| arPriv->arAuthMode = WMI_WPA_AUTH_CCKM; |
| } else if (WMI_WPA2_AUTH == arPriv->arAuthMode) { |
| arPriv->arAuthMode = WMI_WPA2_AUTH_CCKM; |
| } else { |
| ret = -1; |
| } |
| } else if (!(value & IW_AUTH_KEY_MGMT_802_1X)) { |
| arPriv->arAuthMode = WMI_NONE_AUTH; |
| } |
| if(!ret) { |
| if(arPriv->PreAuthKeyMgmt == arPriv->arAuthMode) { |
| profChanged = FALSE; |
| } |
| arPriv->PreAuthKeyMgmt = arPriv->arAuthMode; |
| } |
| break; |
| case IW_AUTH_TKIP_COUNTERMEASURES: |
| wmi_set_tkip_countermeasures_cmd(arPriv->arWmi, value); |
| profChanged = FALSE; |
| break; |
| case IW_AUTH_DROP_UNENCRYPTED: |
| profChanged = FALSE; |
| break; |
| case IW_AUTH_80211_AUTH_ALG: |
| if (value & IW_AUTH_ALG_OPEN_SYSTEM) { |
| if((arPriv->arDot11AuthMode & OPEN_AUTH)) { |
| profChanged = FALSE; |
| } |
| arPriv->arDot11AuthMode = OPEN_AUTH; |
| } |
| if (value & IW_AUTH_ALG_SHARED_KEY) { |
| if((arPriv->arDot11AuthMode & SHARED_AUTH)) { |
| profChanged = FALSE; |
| } |
| arPriv->arDot11AuthMode = SHARED_AUTH; |
| } |
| if (value & IW_AUTH_ALG_LEAP) { |
| if((arPriv->arDot11AuthMode & LEAP_AUTH)) { |
| profChanged = FALSE; |
| } |
| arPriv->arDot11AuthMode = LEAP_AUTH; |
| } |
| if((value & (IW_AUTH_ALG_OPEN_SYSTEM | IW_AUTH_ALG_SHARED_KEY |IW_AUTH_ALG_LEAP))==0){ |
| ret = -1; |
| profChanged = FALSE; |
| } |
| break; |
| case IW_AUTH_WPA_ENABLED: |
| profChanged = FALSE; |
| if (!value) { |
| arPriv->arAuthMode = WMI_NONE_AUTH; |
| /* when the supplicant is stopped, it calls this |
| * handler with value=0. The followings need to be |
| * reset if the STA were to connect again |
| * without security |
| */ |
| arPriv->arDot11AuthMode = OPEN_AUTH; |
| arPriv->arPairwiseCrypto = NONE_CRYPT; |
| arPriv->arPairwiseCryptoLen = 0; |
| arPriv->arGroupCrypto = NONE_CRYPT; |
| arPriv->arGroupCryptoLen = 0; |
| } |
| break; |
| case IW_AUTH_RX_UNENCRYPTED_EAPOL: |
| profChanged = FALSE; |
| break; |
| case IW_AUTH_ROAMING_CONTROL: |
| profChanged = FALSE; |
| break; |
| case IW_AUTH_PRIVACY_INVOKED: |
| profChanged = FALSE; |
| if (!value) { |
| arPriv->arPairwiseCrypto = NONE_CRYPT; |
| arPriv->arPairwiseCryptoLen = 0; |
| arPriv->arGroupCrypto = NONE_CRYPT; |
| arPriv->arGroupCryptoLen = 0; |
| } |
| break; |
| default: |
| ret = -1; |
| profChanged = FALSE; |
| break; |
| } |
| |
| if (profChanged == TRUE) { |
| /* |
| * profile has changed. Erase ssid to signal change |
| */ |
| A_MEMZERO(arPriv->arSsid, sizeof(arPriv->arSsid)); |
| arPriv->arSsidLen = 0; |
| } |
| |
| up(&ar->arSem); |
| return ret; |
| } |
| |
| |
| /* |
| * SIOCGIWAUTH |
| */ |
| static int |
| ar6000_ioctl_giwauth(struct net_device *dev, |
| struct iw_request_info *info, |
| struct iw_param *data, char *extra) |
| { |
| AR_SOFTC_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| |
| A_UINT16 param; |
| A_INT32 ret; |
| |
| if (ar->arWmiReady == FALSE) { |
| return -EIO; |
| } |
| |
| if (ar->arWlanState == WLAN_DISABLED) { |
| return -EIO; |
| } |
| |
| if (down_interruptible(&ar->arSem)) { |
| return -ERESTARTSYS; |
| } |
| |
| if (ar->bIsDestroyProgress || ar->arWlanState == WLAN_DISABLED) { |
| up(&ar->arSem); |
| return -EBUSY; |
| } |
| |
| param = data->flags & IW_AUTH_INDEX; |
| ret = 0; |
| data->value = 0; |
| |
| |
| switch (param) { |
| case IW_AUTH_WPA_VERSION: |
| if (arPriv->arAuthMode == WMI_NONE_AUTH) { |
| data->value |= IW_AUTH_WPA_VERSION_DISABLED; |
| } else if (arPriv->arAuthMode == WMI_WPA_AUTH) { |
| data->value |= IW_AUTH_WPA_VERSION_WPA; |
| } else if (arPriv->arAuthMode == WMI_WPA2_AUTH) { |
| data->value |= IW_AUTH_WPA_VERSION_WPA2; |
| } else { |
| ret = -1; |
| } |
| break; |
| case IW_AUTH_CIPHER_PAIRWISE: |
| if (arPriv->arPairwiseCrypto == NONE_CRYPT) { |
| data->value |= IW_AUTH_CIPHER_NONE; |
| } else if (arPriv->arPairwiseCrypto == WEP_CRYPT) { |
| if (arPriv->arPairwiseCryptoLen == 13) { |
| data->value |= IW_AUTH_CIPHER_WEP104; |
| } else { |
| data->value |= IW_AUTH_CIPHER_WEP40; |
| } |
| } else if (arPriv->arPairwiseCrypto == TKIP_CRYPT) { |
| data->value |= IW_AUTH_CIPHER_TKIP; |
| } else if (arPriv->arPairwiseCrypto == AES_CRYPT) { |
| data->value |= IW_AUTH_CIPHER_CCMP; |
| } else { |
| ret = -1; |
| } |
| break; |
| case IW_AUTH_CIPHER_GROUP: |
| if (arPriv->arGroupCrypto == NONE_CRYPT) { |
| data->value |= IW_AUTH_CIPHER_NONE; |
| } else if (arPriv->arGroupCrypto == WEP_CRYPT) { |
| if (arPriv->arGroupCryptoLen == 13) { |
| data->value |= IW_AUTH_CIPHER_WEP104; |
| } else { |
| data->value |= IW_AUTH_CIPHER_WEP40; |
| } |
| } else if (arPriv->arGroupCrypto == TKIP_CRYPT) { |
| data->value |= IW_AUTH_CIPHER_TKIP; |
| } else if (arPriv->arGroupCrypto == AES_CRYPT) { |
| data->value |= IW_AUTH_CIPHER_CCMP; |
| } else { |
| ret = -1; |
| } |
| break; |
| case IW_AUTH_KEY_MGMT: |
| if ((arPriv->arAuthMode == WMI_WPA_PSK_AUTH) || |
| (arPriv->arAuthMode == WMI_WPA2_PSK_AUTH)) { |
| data->value |= IW_AUTH_KEY_MGMT_PSK; |
| } else if ((arPriv->arAuthMode == WMI_WPA_AUTH) || |
| (arPriv->arAuthMode == WMI_WPA2_AUTH)) { |
| data->value |= IW_AUTH_KEY_MGMT_802_1X; |
| } |
| break; |
| case IW_AUTH_TKIP_COUNTERMEASURES: |
| // TODO. Save countermeassure enable/disable |
| data->value = 0; |
| break; |
| case IW_AUTH_DROP_UNENCRYPTED: |
| break; |
| case IW_AUTH_80211_AUTH_ALG: |
| if (arPriv->arDot11AuthMode == OPEN_AUTH) { |
| data->value |= IW_AUTH_ALG_OPEN_SYSTEM; |
| } else if (arPriv->arDot11AuthMode == SHARED_AUTH) { |
| data->value |= IW_AUTH_ALG_SHARED_KEY; |
| } else if (arPriv->arDot11AuthMode == LEAP_AUTH) { |
| data->value |= IW_AUTH_ALG_LEAP; |
| } else { |
| ret = -1; |
| } |
| break; |
| case IW_AUTH_WPA_ENABLED: |
| if (arPriv->arAuthMode == WMI_NONE_AUTH) { |
| data->value = 0; |
| } else { |
| data->value = 1; |
| } |
| break; |
| case IW_AUTH_RX_UNENCRYPTED_EAPOL: |
| break; |
| case IW_AUTH_ROAMING_CONTROL: |
| break; |
| case IW_AUTH_PRIVACY_INVOKED: |
| if (arPriv->arPairwiseCrypto == NONE_CRYPT) { |
| data->value = 0; |
| } else { |
| data->value = 1; |
| } |
| break; |
| default: |
| ret = -1; |
| break; |
| } |
| |
| up(&ar->arSem); |
| return 0; |
| } |
| |
| /* |
| * SIOCSIWPMKSA |
| */ |
| static int |
| ar6000_ioctl_siwpmksa(struct net_device *dev, |
| struct iw_request_info *info, |
| struct iw_point *data, char *extra) |
| { |
| AR_SOFTC_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| A_INT32 ret; |
| A_STATUS status; |
| struct iw_pmksa *pmksa; |
| |
| pmksa = (struct iw_pmksa *)extra; |
| |
| if (ar->arWmiReady == FALSE || ar->arWlanState == WLAN_DISABLED) { |
| return -EIO; |
| } |
| |
| if (down_interruptible(&ar->arSem)) { |
| return -ERESTARTSYS; |
| } |
| |
| if (ar->bIsDestroyProgress || ar->arWlanState == WLAN_DISABLED) { |
| up(&ar->arSem); |
| return -EBUSY; |
| } |
| |
| ret = 0; |
| status = A_OK; |
| |
| switch (pmksa->cmd) { |
| case IW_PMKSA_ADD: |
| status = wmi_setPmkid_cmd(arPriv->arWmi, (A_UINT8*)pmksa->bssid.sa_data, pmksa->pmkid, TRUE); |
| break; |
| case IW_PMKSA_REMOVE: |
| status = wmi_setPmkid_cmd(arPriv->arWmi, (A_UINT8*)pmksa->bssid.sa_data, pmksa->pmkid, FALSE); |
| break; |
| case IW_PMKSA_FLUSH: |
| if (arPriv->arConnected == TRUE) { |
| status = wmi_setPmkid_cmd(arPriv->arWmi, arPriv->arBssid, NULL, 0); |
| } |
| break; |
| default: |
| ret=-1; |
| break; |
| } |
| if (status != A_OK) { |
| ret = -1; |
| } |
| |
| up(&ar->arSem); |
| return ret; |
| } |
| |
| #ifdef WAPI_ENABLE |
| |
| #define PN_INIT 0x5c365c36 |
| |
| static int ar6000_set_wapi_key(struct net_device *dev, |
| struct iw_request_info *info, |
| struct iw_point *erq, char *extra) |
| { |
| AR_SOFTC_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; |
| KEY_USAGE keyUsage = 0; |
| A_INT32 keyLen; |
| A_UINT8 *keyData; |
| A_INT32 index; |
| A_UINT32 *PN; |
| A_INT32 i; |
| A_STATUS status; |
| A_UINT8 wapiKeyRsc[16]; |
| CRYPTO_TYPE keyType = WAPI_CRYPT; |
| const A_UINT8 broadcastMac[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; |
| |
| index = erq->flags & IW_ENCODE_INDEX; |
| if (index && (((index - 1) < WMI_MIN_KEY_INDEX) || |
| ((index - 1) > WMI_MAX_KEY_INDEX))) { |
| return -EIO; |
| } |
| |
| index--; |
| if (index < 0 || index > 4) { |
| return -EIO; |
| } |
| keyData = (A_UINT8 *)(ext + 1); |
| keyLen = erq->length - sizeof(struct iw_encode_ext); |
| A_MEMCPY(wapiKeyRsc, ext->tx_seq, sizeof(wapiKeyRsc)); |
| |
| if (A_MEMCMP(ext->addr.sa_data, broadcastMac, sizeof(broadcastMac)) == 0) { |
| keyUsage |= GROUP_USAGE; |
| PN = (A_UINT32 *)wapiKeyRsc; |
| for (i = 0; i < 4; i++) { |
| PN[i] = PN_INIT; |
| } |
| } else { |
| keyUsage |= PAIRWISE_USAGE; |
| } |
| status = wmi_addKey_cmd(arPriv->arWmi, |
| index, |
| keyType, |
| keyUsage, |
| keyLen, |
| wapiKeyRsc, |
| keyData, |
| KEY_OP_INIT_WAPIPN, |
| NULL, |
| SYNC_BEFORE_WMIFLAG); |
| if (A_OK != status) { |
| return -EIO; |
| } |
| return 0; |
| } |
| |
| #endif |
| |
| /* |
| * SIOCSIWENCODEEXT |
| */ |
| static int |
| ar6000_ioctl_siwencodeext(struct net_device *dev, |
| struct iw_request_info *info, |
| struct iw_point *erq, char *extra) |
| { |
| AR_SOFTC_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| A_INT32 index; |
| struct iw_encode_ext *ext; |
| KEY_USAGE keyUsage; |
| A_INT32 keyLen; |
| A_UINT8 *keyData; |
| A_UINT8 keyRsc[8]; |
| A_STATUS status; |
| CRYPTO_TYPE keyType; |
| A_UINT32 returnValue = -EIO; |
| #ifdef USER_KEYS |
| struct ieee80211req_key ik; |
| #endif /* USER_KEYS */ |
| AR_SOFTC_AP_T *arAp = &arPriv->arAp; |
| |
| if (ar->arWlanState == WLAN_DISABLED) { |
| return -EIO; |
| } |
| |
| if (down_interruptible(&ar->arSem)) { |
| return -ERESTARTSYS; |
| } |
| |
| if (ar->bIsDestroyProgress || ar->arWlanState == WLAN_DISABLED) { |
| up(&ar->arSem); |
| return -EBUSY; |
| } |
| |
| #ifdef USER_KEYS |
| arPriv->arSta.user_saved_keys.keyOk = FALSE; |
| #endif /* USER_KEYS */ |
| |
| index = erq->flags & IW_ENCODE_INDEX; |
| |
| if (index && (((index - 1) < WMI_MIN_KEY_INDEX) || |
| ((index - 1) > WMI_MAX_KEY_INDEX))) |
| { |
| up(&ar->arSem); |
| return -EIO; |
| } |
| |
| ext = (struct iw_encode_ext *)extra; |
| if (erq->flags & IW_ENCODE_DISABLED) { |
| /* |
| * Encryption disabled |
| */ |
| if (index) { |
| /* |
| * If key index was specified then clear the specified key |
| */ |
| index--; |
| A_MEMZERO(arPriv->arWepKeyList[index].arKey, |
| sizeof(arPriv->arWepKeyList[index].arKey)); |
| arPriv->arWepKeyList[index].arKeyLen = 0; |
| } |
| } else { |
| /* |
| * Enabling WEP encryption |
| */ |
| if (index) { |
| index--; /* keyindex is off base 1 in iwconfig */ |
| } |
| |
| keyUsage = 0; |
| keyLen = erq->length - sizeof(struct iw_encode_ext); |
| |
| if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) { |
| keyUsage = TX_USAGE; |
| arPriv->arDefTxKeyIndex = index; |
| // Just setting the key index |
| if (keyLen == 0) { |
| up(&ar->arSem); |
| return 0; |
| } |
| } |
| |
| if (keyLen <= 0) { |
| if (ext->alg == IW_ENCODE_ALG_KRK) { |
| wmi_delete_krk_cmd(arPriv->arWmi); |
| } |
| up(&ar->arSem); |
| return -EIO; |
| } |
| |
| /* key follows iw_encode_ext */ |
| keyData = (A_UINT8 *)(ext + 1); |
| |
| switch (ext->alg) { |
| case IW_ENCODE_ALG_WEP: |
| keyType = WEP_CRYPT; |
| #ifdef USER_KEYS |
| ik.ik_type = IEEE80211_CIPHER_WEP; |
| #endif /* USER_KEYS */ |
| if(arPriv->arNextMode == AP_NETWORK) { |
| arAp->ap_mode_bkey.ik_type = IEEE80211_CIPHER_WEP; |
| } |
| if (!IEEE80211_IS_VALID_WEP_CIPHER_LEN(keyLen)) { |
| up(&ar->arSem); |
| return -EIO; |
| } |
| |
| /* Check whether it is static wep. */ |
| if (!arPriv->arConnected) { |
| A_MEMZERO(arPriv->arWepKeyList[index].arKey, |
| sizeof(arPriv->arWepKeyList[index].arKey)); |
| A_MEMCPY(arPriv->arWepKeyList[index].arKey, keyData, keyLen); |
| arPriv->arWepKeyList[index].arKeyLen = keyLen; |
| |
| up(&ar->arSem); |
| return 0; |
| } |
| break; |
| case IW_ENCODE_ALG_TKIP: |
| keyType = TKIP_CRYPT; |
| #ifdef USER_KEYS |
| ik.ik_type = IEEE80211_CIPHER_TKIP; |
| #endif /* USER_KEYS */ |
| if(arPriv->arNextMode == AP_NETWORK) { |
| arAp->ap_mode_bkey.ik_type = IEEE80211_CIPHER_TKIP; |
| } |
| break; |
| case IW_ENCODE_ALG_CCMP: |
| keyType = AES_CRYPT; |
| #ifdef USER_KEYS |
| ik.ik_type = IEEE80211_CIPHER_AES_CCM; |
| #endif /* USER_KEYS */ |
| if(arPriv->arNextMode == AP_NETWORK) { |
| arAp->ap_mode_bkey.ik_type = IEEE80211_CIPHER_AES_CCM; |
| } |
| break; |
| #ifdef WAPI_ENABLE |
| case IW_ENCODE_ALG_SM4: |
| { |
| returnValue = ar6000_set_wapi_key(dev, info, erq, extra); |
| up(&ar->arSem); |
| return returnValue; |
| } |
| #endif |
| case IW_ENCODE_ALG_PMK: |
| arPriv->arSta.arConnectCtrlFlags |= CONNECT_DO_WPA_OFFLOAD; |
| returnValue = wmi_set_pmk_cmd(arPriv->arWmi, keyData); |
| up(&ar->arSem); |
| return returnValue; |
| case IW_ENCODE_ALG_KRK: |
| returnValue = wmi_add_krk_cmd(arPriv->arWmi, keyData); |
| up(&ar->arSem); |
| return returnValue; |
| default: |
| up(&ar->arSem); |
| return -EIO; |
| } |
| |
| |
| if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) { |
| keyUsage |= GROUP_USAGE; |
| if(arPriv->arNextMode == AP_NETWORK) { |
| keyUsage &= ~TX_USAGE; |
| arAp->ap_mode_bkey.ik_keyix = index; |
| arAp->ap_mode_bkey.ik_keylen = keyLen; |
| memcpy(arAp->ap_mode_bkey.ik_keydata, keyData, keyLen); |
| memcpy(&arAp->ap_mode_bkey.ik_keyrsc, keyRsc, sizeof(keyRsc)); |
| memcpy(arAp->ap_mode_bkey.ik_macaddr, ext->addr.sa_data, ETH_ALEN); |
| } |
| } else { |
| keyUsage |= PAIRWISE_USAGE; |
| } |
| |
| if (ext->ext_flags & IW_ENCODE_EXT_RX_SEQ_VALID) { |
| A_MEMCPY(keyRsc, ext->rx_seq, sizeof(keyRsc)); |
| } else { |
| A_MEMZERO(keyRsc, sizeof(keyRsc)); |
| } |
| |
| if (((WMI_WPA_PSK_AUTH == arPriv->arAuthMode) || (WMI_WPA2_PSK_AUTH == arPriv->arAuthMode)) && |
| (GROUP_USAGE & keyUsage)) |
| { |
| A_UNTIMEOUT(&arPriv->arSta.disconnect_timer); |
| } |
| |
| status = wmi_addKey_cmd(arPriv->arWmi, index, keyType, keyUsage, |
| keyLen, keyRsc, |
| keyData, KEY_OP_INIT_VAL, |
| (A_UINT8*)ext->addr.sa_data, |
| SYNC_BOTH_WMIFLAG); |
| if (status != A_OK) { |
| up(&ar->arSem); |
| return -EIO; |
| } |
| |
| #ifdef USER_KEYS |
| ik.ik_keyix = index; |
| ik.ik_keylen = keyLen; |
| memcpy(ik.ik_keydata, keyData, keyLen); |
| memcpy(&ik.ik_keyrsc, keyRsc, sizeof(keyRsc)); |
| memcpy(ik.ik_macaddr, ext->addr.sa_data, ETH_ALEN); |
| if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) { |
| memcpy(&arPriv->arSta.user_saved_keys.bcast_ik, &ik, |
| sizeof(struct ieee80211req_key)); |
| } else { |
| memcpy(&arPriv->arSta.user_saved_keys.ucast_ik, &ik, |
| sizeof(struct ieee80211req_key)); |
| } |
| arPriv->arSta.user_saved_keys.keyOk = TRUE; |
| #endif /* USER_KEYS */ |
| } |
| |
| up(&ar->arSem); |
| return 0; |
| } |
| |
| /* |
| * SIOCGIWENCODEEXT |
| */ |
| static int |
| ar6000_ioctl_giwencodeext(struct net_device *dev, |
| struct iw_request_info *info, |
| struct iw_point *erq, char *extra) |
| { |
| AR_SOFTC_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| |
| if (ar->arWlanState == WLAN_DISABLED) { |
| return -EIO; |
| } |
| |
| if (arPriv->arPairwiseCrypto == NONE_CRYPT) { |
| erq->length = 0; |
| erq->flags = IW_ENCODE_DISABLED; |
| } else { |
| erq->length = 0; |
| } |
| |
| return 0; |
| } |
| #endif // WIRELESS_EXT >= 18 |
| |
| #if WIRELESS_EXT > 20 |
| static int ar6000_ioctl_siwpower(struct net_device *dev, |
| struct iw_request_info *info, |
| union iwreq_data *wrqu, char *extra) |
| { |
| #ifndef ATH6K_CONFIG_OTA_MODE |
| AR_SOFTC_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| WMI_POWER_MODE power_mode; |
| |
| if(psm_info == 1 || psm_info == 0) |
| { |
| return 0; |
| } |
| |
| if (ar->arWmiReady == FALSE) { |
| return -EIO; |
| } |
| |
| if (ar->arWlanState == WLAN_DISABLED) { |
| return -EIO; |
| } |
| |
| if (down_interruptible(&ar->arSem)) { |
| return -ERESTARTSYS; |
| } |
| |
| if (ar->bIsDestroyProgress || ar->arWlanState == WLAN_DISABLED) { |
| up(&ar->arSem); |
| return -EBUSY; |
| } |
| |
| if (wrqu->power.disabled) |
| power_mode = MAX_PERF_POWER; |
| else |
| power_mode = REC_POWER; |
| |
| if (wmi_powermode_cmd(arPriv->arWmi, power_mode) < 0) |
| { |
| up(&ar->arSem); |
| return -EIO; |
| } |
| #endif |
| up(&ar->arSem); |
| return 0; |
| } |
| |
| static int ar6000_ioctl_giwpower(struct net_device *dev, |
| struct iw_request_info *info, |
| union iwreq_data *wrqu, char *extra) |
| { |
| AR_SOFTC_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| WMI_POWER_MODE power_mode; |
| |
| if (ar->arWmiReady == FALSE) { |
| return -EIO; |
| } |
| |
| if (ar->arWlanState == WLAN_DISABLED) { |
| return -EIO; |
| } |
| |
| if (down_interruptible(&ar->arSem)) { |
| return -ERESTARTSYS; |
| } |
| |
| if (ar->bIsDestroyProgress || ar->arWlanState == WLAN_DISABLED) { |
| up(&ar->arSem); |
| return -EBUSY; |
| } |
| |
| power_mode = wmi_get_power_mode_cmd(arPriv->arWmi); |
| |
| if (power_mode == MAX_PERF_POWER) |
| wrqu->power.disabled = 1; |
| else |
| wrqu->power.disabled = 0; |
| |
| up(&ar->arSem); |
| return 0; |
| } |
| #endif // WIRELESS_EXT > 20 |
| |
| /* |
| * SIOCGIWNAME |
| */ |
| int |
| ar6000_ioctl_giwname(struct net_device *dev, |
| struct iw_request_info *info, |
| char *name, char *extra) |
| { |
| A_UINT8 capability; |
| AR_SOFTC_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| |
| if (is_iwioctl_allowed(arPriv->arNextMode, info->cmd) != A_OK) { |
| AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("wext_ioctl: cmd=0x%x not allowed in this mode\n", info->cmd)); |
| return -EOPNOTSUPP; |
| } |
| |
| if (ar->arWlanState == WLAN_DISABLED) { |
| return -EIO; |
| } |
| |
| if (down_interruptible(&ar->arSem)) { |
| return -ERESTARTSYS; |
| } |
| |
| if (ar->bIsDestroyProgress || ar->arWlanState == WLAN_DISABLED) { |
| up(&ar->arSem); |
| return -EBUSY; |
| } |
| |
| capability = arPriv->arPhyCapability; |
| if(arPriv->arNetworkType == INFRA_NETWORK && arPriv->arConnected) { |
| bss_t *bss; |
| wmi_scan_report_lock(arPriv->arWmi); |
| bss = wmi_find_node(arPriv->arWmi, arPriv->arBssid); |
| if (bss) { |
| capability = get_bss_phy_capability(bss); |
| wmi_node_return(arPriv->arWmi, bss); |
| } |
| wmi_scan_report_unlock(arPriv->arWmi); |
| } |
| switch (capability) { |
| case (WMI_11A_CAPABILITY): |
| if (strlcpy(name, "AR6000 802.11a", IFNAMSIZ) >= IFNAMSIZ) { |
| AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("strlcpy: AR6000 802.11a\n")); |
| return -1; |
| } |
| break; |
| case (WMI_11G_CAPABILITY): |
| if (strlcpy(name, "AR6000 802.11g", IFNAMSIZ) >= IFNAMSIZ) { |
| AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("strlcpy: AR6000 802.11g\n")); |
| return -1; |
| } |
| break; |
| case (WMI_11AG_CAPABILITY): |
| if (strlcpy(name, "AR6000 802.11ag", IFNAMSIZ) >= IFNAMSIZ) { |
| AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("strlcpy: AR6000 802.11ag\n")); |
| return -1; |
| } |
| break; |
| case (WMI_11NA_CAPABILITY): |
| if (strlcpy(name, "AR6000 802.11na", IFNAMSIZ) >= IFNAMSIZ) { |
| AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("strlcpy: AR6000 802.11na\n")); |
| return -1; |
| } |
| break; |
| case (WMI_11NG_CAPABILITY): |
| if (strlcpy(name, "AR6000 802.11ng", IFNAMSIZ) >= IFNAMSIZ) { |
| AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("strlcpy: AR6000 802.11ng\n")); |
| return -1; |
| } |
| break; |
| case (WMI_11NAG_CAPABILITY): |
| if (strlcpy(name, "AR6K 802.11nag", IFNAMSIZ) >= IFNAMSIZ) { |
| AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("strlcpy: AR6000 802.11nag\n")); |
| return -1; |
| } |
| break; |
| default: |
| if (strlcpy(name, "AR6000 802.11b", IFNAMSIZ) >= IFNAMSIZ) { |
| AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("strlcpy: AR6003 802.11b\n")); |
| return -1; |
| } |
| break; |
| } |
| |
| up(&ar->arSem); |
| return 0; |
| } |
| |
| /* |
| * SIOCSIWFREQ |
| */ |
| int |
| ar6000_ioctl_siwfreq(struct net_device *dev, |
| struct iw_request_info *info, |
| struct iw_freq *freq, char *extra) |
| { |
| AR_SOFTC_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| |
| if (is_iwioctl_allowed(arPriv->arNextMode, info->cmd) != A_OK) { |
| AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("wext_ioctl: cmd=0x%x not allowed in this mode\n", info->cmd)); |
| return -EOPNOTSUPP; |
| } |
| |
| if (ar->arWlanState == WLAN_DISABLED) { |
| return -EIO; |
| } |
| |
| if (down_interruptible(&ar->arSem)) { |
| return -ERESTARTSYS; |
| } |
| |
| if (ar->bIsDestroyProgress || ar->arWlanState == WLAN_DISABLED) { |
| up(&ar->arSem); |
| return -EBUSY; |
| } |
| |
| /* |
| * 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) { |
| up(&ar->arSem); |
| return -EINVAL; |
| } else if (freq->e == 1) { |
| if ((arPriv->arPhyCapability == WMI_11NG_CAPABILITY) && (((freq->m / 100000) >= 5180) && ((freq->m / 100000) <= 5825))) { |
| up(&ar->arSem); |
| return -EINVAL; |
| } |
| arPriv->arChannelHint = freq->m / 100000; |
| } else if(freq->m > 0) { |
| if ((arPriv->arPhyCapability == WMI_11NG_CAPABILITY) && ((wlan_ieee2freq(freq->m) >=5180) && (wlan_ieee2freq(freq->m) <= 5825))) { |
| up(&ar->arSem); |
| return -EINVAL; |
| } |
| arPriv->arChannelHint = wlan_ieee2freq(freq->m); |
| } else { |
| /* Auto Channel Selection */ |
| arPriv->arChannelHint = 0; |
| } |
| |
| if(ar->lteFreq && arPriv->arChannelHint && arPriv->arNetworkType == AP_NETWORK) { |
| A_PRINTF("LTE coex enabled. Ignore channel set to %d\n", arPriv->arChannelHint); |
| arPriv->arChannelHint = 0; |
| } else { |
| arPriv->ap_profile_flag = 1; /* There is a change in profile */ |
| A_PRINTF("channel hint set to %d\n", arPriv->arChannelHint); |
| } |
| |
| up(&ar->arSem); |
| return 0; |
| } |
| |
| /* |
| * SIOCGIWFREQ |
| */ |
| int |
| ar6000_ioctl_giwfreq(struct net_device *dev, |
| struct iw_request_info *info, |
| struct iw_freq *freq, char *extra) |
| { |
| AR_SOFTC_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| |
| if (is_iwioctl_allowed(arPriv->arNextMode, info->cmd) != A_OK) { |
| AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("wext_ioctl: cmd=0x%x not allowed in this mode\n", info->cmd)); |
| return -EOPNOTSUPP; |
| } |
| |
| if (ar->arWlanState == WLAN_DISABLED) { |
| return -EIO; |
| } |
| |
| if (down_interruptible(&ar->arSem)) { |
| return -ERESTARTSYS; |
| } |
| |
| if (ar->bIsDestroyProgress || ar->arWlanState == WLAN_DISABLED) { |
| up(&ar->arSem); |
| return -EBUSY; |
| } |
| |
| if (arPriv->arNetworkType == AP_NETWORK) { |
| if(arPriv->arChannelHint) { |
| freq->m = arPriv->arChannelHint * 100000; |
| } else if(arPriv->arBssChannel) { |
| freq->m = arPriv->arBssChannel * 100000; |
| } else { |
| up(&ar->arSem); |
| return -EINVAL; |
| } |
| } else { |
| if (arPriv->arConnected != TRUE) { |
| up(&ar->arSem); |
| return -EINVAL; |
| } else { |
| freq->m = arPriv->arBssChannel * 100000; |
| } |
| } |
| |
| freq->e = 1; |
| |
| up(&ar->arSem); |
| return 0; |
| } |
| |
| /* |
| * SIOCSIWMODE |
| */ |
| int |
| ar6000_ioctl_siwmode(struct net_device *dev, |
| struct iw_request_info *info, |
| __u32 *mode, char *extra) |
| { |
| AR_SOFTC_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| |
| if (is_iwioctl_allowed(arPriv->arNextMode, info->cmd) != A_OK) { |
| AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("wext_ioctl: cmd=0x%x not allowed in this mode\n", info->cmd)); |
| return -EOPNOTSUPP; |
| } |
| |
| if (ar->arWlanState == WLAN_DISABLED) { |
| return -EIO; |
| } |
| |
| if (down_interruptible(&ar->arSem)) { |
| return -ERESTARTSYS; |
| } |
| |
| if (ar->bIsDestroyProgress || ar->arWlanState == WLAN_DISABLED) { |
| up(&ar->arSem); |
| return -EBUSY; |
| } |
| |
| #if 0 |
| /* |
| * clear SSID during mode switch in connected state |
| */ |
| if(!(arPriv->arNetworkType == (((*mode) == IW_MODE_INFRA) ? INFRA_NETWORK : ADHOC_NETWORK)) |
| && (arPriv->arConnected == TRUE) ){ |
| A_MEMZERO(arPriv->arSsid, sizeof(arPriv->arSsid)); |
| arPriv->arSsidLen = 0; |
| } |
| #endif |
| |
| switch (*mode) { |
| case IW_MODE_INFRA: |
| arPriv->arNextMode = INFRA_NETWORK; |
| break; |
| case IW_MODE_ADHOC: |
| arPriv->arNextMode = ADHOC_NETWORK; |
| break; |
| case IW_MODE_MASTER: |
| arPriv->arNextMode = AP_NETWORK; |
| break; |
| default: |
| up(&ar->arSem); |
| return -EINVAL; |
| } |
| |
| if (arPriv->arNetworkType != arPriv->arNextMode) { |
| ar6000_init_mode_info(arPriv); |
| } |
| |
| /* Update the arNetworkType */ |
| arPriv->arNetworkType = arPriv->arNextMode; |
| |
| up(&ar->arSem); |
| return 0; |
| } |
| |
| /* |
| * SIOCGIWMODE |
| */ |
| int |
| ar6000_ioctl_giwmode(struct net_device *dev, |
| struct iw_request_info *info, |
| __u32 *mode, char *extra) |
| { |
| AR_SOFTC_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| |
| if (is_iwioctl_allowed(arPriv->arNextMode, info->cmd) != A_OK) { |
| AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("wext_ioctl: cmd=0x%x not allowed in this mode\n", info->cmd)); |
| return -EOPNOTSUPP; |
| } |
| |
| if (ar->arWlanState == WLAN_DISABLED) { |
| return -EIO; |
| } |
| |
| switch (arPriv->arNetworkType) { |
| case INFRA_NETWORK: |
| *mode = IW_MODE_INFRA; |
| break; |
| case ADHOC_NETWORK: |
| *mode = IW_MODE_ADHOC; |
| break; |
| case AP_NETWORK: |
| *mode = IW_MODE_MASTER; |
| 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_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| AR_SOFTC_STA_T *arSta = &arPriv->arSta; |
| struct iw_range *range = (struct iw_range *) extra; |
| int i, j, ret = 0; |
| const A_INT32 rateTable[] = { |
| 1000, 2000, 5500, 11000, |
| 6000, 9000, 12000, 18000, 24000, 36000, 48000, 54000, |
| 6500, 13000, 19500, 26000, 39000, 52000, 58500, 65000 }; |
| |
| if (is_iwioctl_allowed(arPriv->arNextMode, info->cmd) != A_OK) { |
| AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("wext_ioctl: cmd=0x%x not allowed in this mode\n", info->cmd)); |
| return -EOPNOTSUPP; |
| } |
| |
| if (ar->bIsDestroyProgress) { |
| return -EBUSY; |
| } |
| |
| if (ar->arWmiReady == FALSE || ar->arWlanState == WLAN_DISABLED) { |
| return -EIO; |
| } |
| |
| if (down_interruptible(&ar->arSem)) { |
| return -ERESTARTSYS; |
| } |
| |
| if (ar->bIsDestroyProgress || ar->arWlanState == WLAN_DISABLED) { |
| up(&ar->arSem); |
| return -EBUSY; |
| } |
| |
| arSta->arNumChannels = -1; |
| A_MEMZERO(arSta->arChannelList, sizeof (arSta->arChannelList)); |
| |
| if (wmi_get_channelList_cmd(arPriv->arWmi) != A_OK) { |
| up(&ar->arSem); |
| return -EIO; |
| } |
| |
| wait_event_interruptible_timeout(arPriv->arEvent, arSta->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->enc_capa |= IW_ENC_CAPA_WPA | IW_ENC_CAPA_WPA2; |
| range->enc_capa |= IW_ENC_CAPA_CIPHER_TKIP|IW_ENC_CAPA_CIPHER_CCMP; |
| /* Event capability (kernel + driver) */ |
| range->event_capa[0] = (IW_EVENT_CAPA_K_0 | |
| IW_EVENT_CAPA_MASK(SIOCGIWAP) | |
| IW_EVENT_CAPA_MASK(SIOCGIWSCAN)); |
| range->event_capa[1] = IW_EVENT_CAPA_K_1; |
| range->event_capa[4] = (IW_EVENT_CAPA_MASK(IWEVCUSTOM) | |
| IW_EVENT_CAPA_MASK(IWEVREGISTERED) | |
| IW_EVENT_CAPA_MASK(IWEVEXPIRED) | |
| #if WIRELESS_EXT >= 18 |
| IW_EVENT_CAPA_MASK(IWEVPMKIDCAND) | |
| IW_EVENT_CAPA_MASK(IWEVASSOCRESPIE) | |
| IW_EVENT_CAPA_MASK(IWEVASSOCREQIE) | |
| IW_EVENT_CAPA_MASK(IWEVGENIE) | |
| #endif |
| 0); |
| |
| range->scan_capa = IW_SCAN_CAPA_ESSID | IW_SCAN_CAPA_CHANNEL; |
| |
| range->txpower_capa = 0; |
| |
| 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 = arSta->arNumChannels; |
| for (i = 0; i < arSta->arNumChannels; i++) { |
| range->freq[i].i = wlan_freq2ieee(arSta->arChannelList[i]); |
| range->freq[i].m = arSta->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 */ |
| |
| for (i=0, j=0; i<sizeof(rateTable)/sizeof(rateTable[0]) && j<IW_MAX_BITRATES; ++i) { |
| A_INT8 rateIdx; |
| if (wmi_validate_bitrate(arPriv->arWmi, rateTable[i], &rateIdx)==A_OK) { |
| range->bitrate[j] = wmi_get_rate(i) * 1000; |
| ++j; |
| } |
| } |
| range->num_bitrates = j; |
| |
| /* 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; |
| |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) |
| if (arSta->wpaOffloadEnabled) { |
| range->enc_capa |= IW_ENC_CAPA_4WAY_HANDSHAKE; |
| } |
| #endif /* LINUX_VERSION_CODE >= 2.6.27 */ |
| |
| up(&ar->arSem); |
| |
| return ret; |
| } |
| |