| /* |
| * --------------------------------------------------------------------------- |
| * FILE: sme_wext.c |
| * |
| * PURPOSE: |
| * Handlers for ioctls from iwconfig. |
| * These provide the control plane operations. |
| * |
| * Copyright (C) 2007-2009 by Cambridge Silicon Radio Ltd. |
| * |
| * Refer to LICENSE.txt included with this source code for details on |
| * the license terms. |
| * |
| * --------------------------------------------------------------------------- |
| */ |
| #include <linux/types.h> |
| #include <linux/etherdevice.h> |
| #include <linux/if_arp.h> |
| #include <asm/uaccess.h> |
| #include <linux/ctype.h> |
| #include "unifi_priv.h" |
| #include <linux/rtnetlink.h> |
| |
| #define CHECK_INITED(_priv) \ |
| do { \ |
| if (_priv->init_progress != UNIFI_INIT_COMPLETED) { \ |
| unifi_trace(_priv, UDBG2, "%s unifi not ready, failing wext call\n", __FUNCTION__); \ |
| return -ENODEV; \ |
| } \ |
| } while (0) |
| |
| /* Workaround for the wpa_supplicant hanging issue - disabled on Android */ |
| #ifndef ANDROID_BUILD |
| #define CSR_WIFI_WEXT_HANG_WORKAROUND |
| #endif |
| |
| #ifdef CSR_WIFI_WEXT_HANG_WORKAROUND |
| # define UF_RTNL_LOCK() rtnl_lock() |
| # define UF_RTNL_UNLOCK() rtnl_unlock() |
| #else |
| # define UF_RTNL_LOCK() |
| # define UF_RTNL_UNLOCK() |
| #endif |
| |
| |
| /* |
| * --------------------------------------------------------------------------- |
| * Helper functions |
| * --------------------------------------------------------------------------- |
| */ |
| |
| /* |
| * --------------------------------------------------------------------------- |
| * wext_freq_to_channel |
| * channel_to_mhz |
| * |
| * These functions convert between channel number and frequency. |
| * |
| * Arguments: |
| * ch Channel number, as defined in 802.11 specs |
| * m, e Mantissa and exponent as provided by wireless extension. |
| * |
| * Returns: |
| * channel or frequency (in MHz) value |
| * --------------------------------------------------------------------------- |
| */ |
| static int |
| wext_freq_to_channel(int m, int e) |
| { |
| int mhz; |
| |
| mhz = m; |
| while (e < 6) { |
| mhz /= 10; |
| e++; |
| } |
| while (e > 6) { |
| mhz *= 10; |
| e--; |
| } |
| |
| if (mhz >= 5000) { |
| return ((mhz - 5000) / 5); |
| } |
| |
| if (mhz == 2482) { |
| return 14; |
| } |
| |
| if (mhz >= 2407) { |
| return ((mhz - 2407) / 5); |
| } |
| |
| return 0; |
| } /* wext_freq_to_channel() */ |
| |
| static int |
| channel_to_mhz(int ch, int dot11a) |
| { |
| |
| if (ch == 0) return 0; |
| if (ch > 200) return 0; |
| |
| /* 5G */ |
| if (dot11a) { |
| return (5000 + (5 * ch)); |
| } |
| |
| /* 2.4G */ |
| if (ch == 14) { |
| return 2484; |
| } |
| |
| if ((ch < 14) && (ch > 0)) { |
| return (2407 + (5 * ch)); |
| } |
| |
| return 0; |
| } |
| #ifdef CSR_SUPPORT_WEXT_AP |
| void uf_sme_wext_ap_set_defaults(unifi_priv_t *priv) |
| { |
| memcpy(priv->ap_config.ssid.ssid,"defaultssid",sizeof("defaultssid")); |
| |
| priv->ap_config.ssid.length = 8; |
| priv->ap_config.channel = 6; |
| priv->ap_config.if_index = 1; |
| priv->ap_config.credentials.authType = 0; |
| priv->ap_config.max_connections=8; |
| |
| priv->group_sec_config.apGroupkeyTimeout = 0; |
| priv->group_sec_config.apStrictGtkRekey = 0; |
| priv->group_sec_config.apGmkTimeout = 0; |
| priv->group_sec_config.apResponseTimeout = 100; /* Default*/ |
| priv->group_sec_config.apRetransLimit = 3; /* Default*/ |
| /* Set default params even if they may not be used*/ |
| /* Until Here*/ |
| |
| priv->ap_mac_config.preamble = CSR_WIFI_SME_USE_LONG_PREAMBLE; |
| priv->ap_mac_config.shortSlotTimeEnabled = FALSE; |
| priv->ap_mac_config.ctsProtectionType=CSR_WIFI_SME_CTS_PROTECTION_AUTOMATIC; |
| |
| priv->ap_mac_config.wmmEnabled = TRUE; |
| priv->ap_mac_config.wmmApParams[0].cwMin=4; |
| priv->ap_mac_config.wmmApParams[0].cwMax=10; |
| priv->ap_mac_config.wmmApParams[0].aifs=3; |
| priv->ap_mac_config.wmmApParams[0].txopLimit=0; |
| priv->ap_mac_config.wmmApParams[0].admissionControlMandatory=FALSE; |
| priv->ap_mac_config.wmmApParams[1].cwMin=4; |
| priv->ap_mac_config.wmmApParams[1].cwMax=10; |
| priv->ap_mac_config.wmmApParams[1].aifs=7; |
| priv->ap_mac_config.wmmApParams[1].txopLimit=0; |
| priv->ap_mac_config.wmmApParams[1].admissionControlMandatory=FALSE; |
| priv->ap_mac_config.wmmApParams[2].cwMin=3; |
| priv->ap_mac_config.wmmApParams[2].cwMax=4; |
| priv->ap_mac_config.wmmApParams[2].aifs=1; |
| priv->ap_mac_config.wmmApParams[2].txopLimit=94; |
| priv->ap_mac_config.wmmApParams[2].admissionControlMandatory=FALSE; |
| priv->ap_mac_config.wmmApParams[3].cwMin=2; |
| priv->ap_mac_config.wmmApParams[3].cwMax=3; |
| priv->ap_mac_config.wmmApParams[3].aifs=1; |
| priv->ap_mac_config.wmmApParams[3].txopLimit=47; |
| priv->ap_mac_config.wmmApParams[3].admissionControlMandatory=FALSE; |
| |
| priv->ap_mac_config.wmmApBcParams[0].cwMin=4; |
| priv->ap_mac_config.wmmApBcParams[0].cwMax=10; |
| priv->ap_mac_config.wmmApBcParams[0].aifs=3; |
| priv->ap_mac_config.wmmApBcParams[0].txopLimit=0; |
| priv->ap_mac_config.wmmApBcParams[0].admissionControlMandatory=FALSE; |
| priv->ap_mac_config.wmmApBcParams[1].cwMin=4; |
| priv->ap_mac_config.wmmApBcParams[1].cwMax=10; |
| priv->ap_mac_config.wmmApBcParams[1].aifs=7; |
| priv->ap_mac_config.wmmApBcParams[1].txopLimit=0; |
| priv->ap_mac_config.wmmApBcParams[1].admissionControlMandatory=FALSE; |
| priv->ap_mac_config.wmmApBcParams[2].cwMin=3; |
| priv->ap_mac_config.wmmApBcParams[2].cwMax=4; |
| priv->ap_mac_config.wmmApBcParams[2].aifs=2; |
| priv->ap_mac_config.wmmApBcParams[2].txopLimit=94; |
| priv->ap_mac_config.wmmApBcParams[2].admissionControlMandatory=FALSE; |
| priv->ap_mac_config.wmmApBcParams[3].cwMin=2; |
| priv->ap_mac_config.wmmApBcParams[3].cwMax=3; |
| priv->ap_mac_config.wmmApBcParams[3].aifs=2; |
| priv->ap_mac_config.wmmApBcParams[3].txopLimit=47; |
| priv->ap_mac_config.wmmApBcParams[3].admissionControlMandatory=FALSE; |
| |
| priv->ap_mac_config.accessType=CSR_WIFI_AP_ACCESS_TYPE_NONE; |
| priv->ap_mac_config.macAddressListCount=0; |
| priv->ap_mac_config.macAddressList=NULL; |
| |
| priv->ap_mac_config.apHtParams.rxStbc=1; |
| priv->ap_mac_config.apHtParams.rifsModeAllowed=TRUE; |
| priv->ap_mac_config.apHtParams.greenfieldSupported=FALSE; |
| priv->ap_mac_config.apHtParams.shortGi20MHz=TRUE; |
| priv->ap_mac_config.apHtParams.htProtection=0; |
| priv->ap_mac_config.apHtParams.dualCtsProtection=FALSE; |
| |
| priv->ap_mac_config.phySupportedBitmap = |
| (CSR_WIFI_SME_AP_PHY_SUPPORT_B|CSR_WIFI_SME_AP_PHY_SUPPORT_G|CSR_WIFI_SME_AP_PHY_SUPPORT_N); |
| priv->ap_mac_config.beaconInterval= 100; |
| priv->ap_mac_config.dtimPeriod=3; |
| priv->ap_mac_config.maxListenInterval=0x00ff;/* Set it to a large value |
| to enable different types of |
| devices to join us */ |
| priv->ap_mac_config.supportedRatesCount = |
| uf_configure_supported_rates(priv->ap_mac_config.supportedRates,priv->ap_mac_config.phySupportedBitmap); |
| } |
| #endif |
| /* |
| * --------------------------------------------------------------------------- |
| * uf_sme_wext_set_defaults |
| * |
| * Set up power-on defaults for driver config. |
| * |
| * Note: The SME Management API *cannot* be used in this function. |
| * |
| * Arguments: |
| * priv Pointer to device private context struct |
| * |
| * Returns: |
| * None. |
| * --------------------------------------------------------------------------- |
| */ |
| void |
| uf_sme_wext_set_defaults(unifi_priv_t *priv) |
| { |
| memset(&priv->connection_config, 0, sizeof(CsrWifiSmeConnectionConfig)); |
| |
| priv->connection_config.bssType = CSR_WIFI_SME_BSS_TYPE_INFRASTRUCTURE; |
| priv->connection_config.authModeMask = CSR_WIFI_SME_AUTH_MODE_80211_OPEN; |
| priv->connection_config.encryptionModeMask = CSR_WIFI_SME_ENCRYPTION_CIPHER_NONE; |
| priv->connection_config.privacyMode = CSR_WIFI_SME_80211_PRIVACY_MODE_DISABLED; |
| priv->connection_config.wmmQosInfo = 0xFF; |
| priv->connection_config.ifIndex = CSR_WIFI_SME_RADIO_IF_BOTH; |
| priv->connection_config.adhocJoinOnly = FALSE; |
| priv->connection_config.adhocChannel = 6; |
| |
| priv->wep_tx_key_index = 0; |
| |
| priv->wext_wireless_stats.qual.qual = 0; |
| priv->wext_wireless_stats.qual.level = 0; |
| priv->wext_wireless_stats.qual.noise = 0; |
| priv->wext_wireless_stats.qual.updated = 0x70; |
| #ifdef CSR_SUPPORT_WEXT_AP |
| /* Initialize the default configuration for AP */ |
| uf_sme_wext_ap_set_defaults(priv); |
| #endif |
| |
| |
| } /* uf_sme_wext_set_defaults() */ |
| |
| |
| /* |
| * --------------------------------------------------------------------------- |
| * WEXT methods |
| * --------------------------------------------------------------------------- |
| */ |
| |
| /* |
| * --------------------------------------------------------------------------- |
| * unifi_giwname - handler for SIOCGIWNAME |
| * unifi_siwfreq - handler for SIOCSIWFREQ |
| * unifi_giwfreq - handler for SIOCGIWFREQ |
| * unifi_siwmode - handler for SIOCSIWMODE |
| * unifi_giwmode - handler for SIOCGIWMODE |
| * unifi_giwrange - handler for SIOCGIWRANGE |
| * unifi_siwap - handler for SIOCSIWAP |
| * unifi_giwap - handler for SIOCGIWAP |
| * unifi_siwscan - handler for SIOCSIWSCAN |
| * unifi_giwscan - handler for SIOCGIWSCAN |
| * unifi_siwessid - handler for SIOCSIWESSID |
| * unifi_giwessid - handler for SIOCGIWESSID |
| * unifi_siwencode - handler for SIOCSIWENCODE |
| * unifi_giwencode - handler for SIOCGIWENCODE |
| * |
| * Handler functions for IW extensions. |
| * These are registered via the unifi_iw_handler_def struct below |
| * and called by the generic IW driver support code. |
| * See include/net/iw_handler.h. |
| * |
| * Arguments: |
| * None. |
| * |
| * Returns: |
| * None. |
| * --------------------------------------------------------------------------- |
| */ |
| static int |
| iwprivsdefs(struct net_device *dev, struct iw_request_info *info, |
| union iwreq_data *wrqu, char *extra) |
| { |
| int r; |
| netInterface_priv_t *interfacePriv = (netInterface_priv_t *)netdev_priv(dev); |
| unifi_priv_t *priv = interfacePriv->privPtr; |
| CsrWifiSmeMibConfig mibConfig; |
| CsrWifiSmePowerConfig powerConfig; |
| |
| unifi_trace(priv, UDBG1, "iwprivs80211defaults: reload defaults\n"); |
| |
| uf_sme_wext_set_defaults(priv); |
| |
| /* Get, modify and set the MIB data */ |
| r = sme_mgt_mib_config_get(priv, &mibConfig); |
| if (r) { |
| unifi_error(priv, "iwprivs80211defaults: Get CsrWifiSmeMibConfigValue failed.\n"); |
| return r; |
| } |
| mibConfig.dot11RtsThreshold = 2347; |
| mibConfig.dot11FragmentationThreshold = 2346; |
| r = sme_mgt_mib_config_set(priv, &mibConfig); |
| if (r) { |
| unifi_error(priv, "iwprivs80211defaults: Set CsrWifiSmeMibConfigValue failed.\n"); |
| return r; |
| } |
| |
| powerConfig.powerSaveLevel = CSR_WIFI_SME_POWER_SAVE_LEVEL_LOW; |
| powerConfig.listenIntervalTu = 100; |
| powerConfig.rxDtims = 1; |
| |
| r = sme_mgt_power_config_set(priv, &powerConfig); |
| if (r) { |
| unifi_error(priv, "iwprivs80211defaults: Set unifi_PowerConfigValue failed.\n"); |
| return r; |
| } |
| |
| return 0; |
| } /* iwprivsdefs() */ |
| |
| static int |
| iwprivs80211ps(struct net_device *dev, struct iw_request_info *info, |
| union iwreq_data *wrqu, char *extra) |
| { |
| int r = 0; |
| netInterface_priv_t *interfacePriv = (netInterface_priv_t *)netdev_priv(dev); |
| unifi_priv_t *priv = interfacePriv->privPtr; |
| |
| int ps_mode = (int)(*extra); |
| CsrWifiSmePowerConfig powerConfig; |
| |
| unifi_trace(priv, UDBG1, "iwprivs80211ps: power save mode = %d\n", ps_mode); |
| |
| r = sme_mgt_power_config_get(priv, &powerConfig); |
| if (r) { |
| unifi_error(priv, "iwprivs80211ps: Get unifi_PowerConfigValue failed.\n"); |
| return r; |
| } |
| |
| switch (ps_mode) { |
| case CSR_PMM_ACTIVE_MODE: |
| powerConfig.powerSaveLevel = CSR_WIFI_SME_POWER_SAVE_LEVEL_LOW; |
| break; |
| case CSR_PMM_POWER_SAVE: |
| powerConfig.powerSaveLevel = CSR_WIFI_SME_POWER_SAVE_LEVEL_HIGH; |
| break; |
| case CSR_PMM_FAST_POWER_SAVE: |
| powerConfig.powerSaveLevel = CSR_WIFI_SME_POWER_SAVE_LEVEL_MED; |
| break; |
| default: |
| powerConfig.powerSaveLevel = CSR_WIFI_SME_POWER_SAVE_LEVEL_AUTO; |
| break; |
| } |
| |
| r = sme_mgt_power_config_set(priv, &powerConfig); |
| if (r) { |
| unifi_error(priv, "iwprivs80211ps: Set unifi_PowerConfigValue failed.\n"); |
| } |
| |
| return r; |
| } |
| |
| static int |
| iwprivg80211ps(struct net_device *dev, struct iw_request_info *info, |
| union iwreq_data *wrqu, char *extra) |
| { |
| netInterface_priv_t *interfacePriv = (netInterface_priv_t *)netdev_priv(dev); |
| unifi_priv_t *priv = interfacePriv->privPtr; |
| |
| CsrWifiSmePowerConfig powerConfig; |
| int r; |
| |
| r = sme_mgt_power_config_get(priv, &powerConfig); |
| if (r) { |
| unifi_error(priv, "iwprivg80211ps: Get 802.11 power mode failed.\n"); |
| return r; |
| } |
| |
| switch (powerConfig.powerSaveLevel) { |
| case CSR_WIFI_SME_POWER_SAVE_LEVEL_LOW: |
| snprintf(extra, IWPRIV_POWER_SAVE_MAX_STRING, |
| "Power save mode: %d (Active)", |
| powerConfig.powerSaveLevel); |
| break; |
| case CSR_WIFI_SME_POWER_SAVE_LEVEL_MED: |
| snprintf(extra, IWPRIV_POWER_SAVE_MAX_STRING, |
| "Power save mode: %d (Fast)", |
| powerConfig.powerSaveLevel); |
| break; |
| case CSR_WIFI_SME_POWER_SAVE_LEVEL_HIGH: |
| snprintf(extra, IWPRIV_POWER_SAVE_MAX_STRING, |
| "Power save mode: %d (Full)", |
| powerConfig.powerSaveLevel); |
| break; |
| case CSR_WIFI_SME_POWER_SAVE_LEVEL_AUTO: |
| snprintf(extra, IWPRIV_POWER_SAVE_MAX_STRING, |
| "Power save mode: %d (Auto)", |
| powerConfig.powerSaveLevel); |
| break; |
| default: |
| snprintf(extra, IWPRIV_POWER_SAVE_MAX_STRING, |
| "Power save mode: %d (Unknown)", |
| powerConfig.powerSaveLevel); |
| break; |
| } |
| |
| wrqu->data.length = strlen(extra) + 1; |
| |
| return 0; |
| } |
| |
| static int |
| iwprivssmedebug(struct net_device *dev, struct iw_request_info *info, |
| union iwreq_data *wrqu, char *extra) |
| { |
| /* No longer supported on the API */ |
| #if defined (CSR_WIFI_HIP_DEBUG_OFFLINE) |
| unifi_debug_buf_dump(); |
| #endif |
| |
| return 0; |
| } |
| |
| #ifdef CSR_SUPPORT_WEXT_AP |
| #define PARAM_TYPE_INT 0 |
| #define PARAM_TYPE_STRING 1 |
| #define CSR_WIFI_MAX_SSID_LEN 32 |
| #define CSR_WIFI_MAX_SEC_LEN 16 |
| #define CSR_WIFI_MAX_KEY_LEN 65 |
| |
| static int hex_look_up(char x) |
| { |
| if(x>='0' && x<='9') |
| return (x-48); |
| if(x>= 'a' && x <= 'f') |
| return (x-87); |
| return -1; |
| } |
| |
| static int power (int a, int b) |
| { |
| int i; |
| int num =1; |
| for(i=0;i<b;i++) |
| num *=a; |
| return num; |
| } |
| |
| static int decode_parameter_from_string(unifi_priv_t* priv, char **str_ptr, |
| const char *token, int param_type, |
| void *dst, int param_max_len) |
| { |
| u8 int_str[7] = "0"; |
| u32 param_str_len; |
| u8 *param_str_begin,*param_str_end; |
| u8 *orig_str = *str_ptr; |
| |
| if (!strncmp(*str_ptr, token, strlen(token))) { |
| strsep(str_ptr, "=,"); |
| param_str_begin = *str_ptr; |
| strsep(str_ptr, "=,"); |
| if (*str_ptr == NULL) { |
| param_str_len = strlen(param_str_begin); |
| } else { |
| param_str_end = *str_ptr-1; |
| param_str_len = param_str_end - param_str_begin; |
| } |
| unifi_trace(priv,UDBG2,"'token:%s', len:%d, ", token, param_str_len); |
| if (param_str_len > param_max_len) { |
| unifi_notice(priv,"extracted param len:%d is > MAX:%d\n",param_str_len, param_max_len); |
| param_str_len = param_max_len; |
| } |
| switch (param_type) { |
| case PARAM_TYPE_INT: |
| { |
| u32 *pdst_int = dst,num =0; |
| int i,j=0; |
| if (param_str_len > sizeof(int_str)) { |
| param_str_len = sizeof(int_str); |
| } |
| memcpy(int_str, param_str_begin, param_str_len); |
| for(i = param_str_len; i>0;i--) { |
| if(int_str[i-1] >= '0' && int_str[i-1] <='9') { |
| num += ((int_str[i-1]-'0')*power(10,j)); |
| j++; |
| } else { |
| unifi_error(priv,"decode_parameter_from_string:not a number %c\n",(int_str[i-1])); |
| return -1; |
| } |
| } |
| *pdst_int = num; |
| unifi_trace(priv,UDBG2,"decode_parameter_from_string:decoded int = %d\n",*pdst_int); |
| } |
| break; |
| default: |
| memcpy(dst, param_str_begin, param_str_len); |
| *((char *)dst + param_str_len) = 0; |
| unifi_trace(priv,UDBG2,"decode_parameter_from_string:decoded string = %s\n",(char *)dst); |
| break; |
| } |
| } else { |
| unifi_error(priv,"decode_parameter_from_string: Token:%s not found in %s \n",token,orig_str); |
| return -1; |
| } |
| return 0; |
| } |
| static int store_ap_advanced_config_from_string(unifi_priv_t *priv, char *param_str) |
| { |
| char * str_ptr=param_str; |
| int ret = 0,tmp_var; |
| char phy_mode[6]; |
| CsrWifiSmeApMacConfig * ap_mac_config = &priv->ap_mac_config; |
| |
| /* Check for BI */ |
| ret = decode_parameter_from_string(priv, &str_ptr, "BI=", |
| PARAM_TYPE_INT, &tmp_var, 5); |
| if(ret) { |
| unifi_error(priv,"store_ap_advanced_config_from_string: BI not found\n"); |
| return -1; |
| } |
| ap_mac_config->beaconInterval = tmp_var; |
| ret = decode_parameter_from_string(priv, &str_ptr, "DTIM_PER=", |
| PARAM_TYPE_INT, &tmp_var, 5); |
| if(ret) { |
| unifi_error(priv,"store_ap_advanced_config_from_string: DTIM_PER not found\n"); |
| return -1; |
| } |
| ap_mac_config->dtimPeriod = tmp_var; |
| ret = decode_parameter_from_string(priv, &str_ptr, "WMM=", |
| PARAM_TYPE_INT, &tmp_var, 5); |
| if(ret) { |
| unifi_error(priv,"store_ap_advanced_config_from_string: WMM not found\n"); |
| return -1; |
| } |
| ap_mac_config->wmmEnabled = tmp_var; |
| ret = decode_parameter_from_string(priv, &str_ptr, "PHY=", |
| PARAM_TYPE_STRING, phy_mode, 5); |
| if(ret) { |
| unifi_error(priv,"store_ap_advanced_config_from_string: PHY not found\n"); |
| } else { |
| if(strstr(phy_mode,"b")){ |
| ap_mac_config->phySupportedBitmap = CSR_WIFI_SME_AP_PHY_SUPPORT_B; |
| } |
| if(strstr(phy_mode,"g")) { |
| ap_mac_config->phySupportedBitmap |= CSR_WIFI_SME_AP_PHY_SUPPORT_G; |
| } |
| if(strstr(phy_mode,"n")) { |
| ap_mac_config->phySupportedBitmap |= CSR_WIFI_SME_AP_PHY_SUPPORT_N; |
| } |
| ap_mac_config->supportedRatesCount = |
| uf_configure_supported_rates(ap_mac_config->supportedRates, ap_mac_config->phySupportedBitmap); |
| } |
| return ret; |
| } |
| |
| static int store_ap_config_from_string( unifi_priv_t * priv,char *param_str) |
| |
| { |
| char *str_ptr = param_str; |
| char sub_cmd[16]; |
| char sec[CSR_WIFI_MAX_SEC_LEN]; |
| char key[CSR_WIFI_MAX_KEY_LEN]; |
| int ret = 0,tmp_var; |
| CsrWifiSmeApConfig_t *ap_config = &priv->ap_config; |
| CsrWifiSmeApMacConfig * ap_mac_config = &priv->ap_mac_config; |
| memset(sub_cmd, 0, sizeof(sub_cmd)); |
| if(!strstr(param_str,"END")) { |
| unifi_error(priv,"store_ap_config_from_string:Invalid config string:%s\n",param_str); |
| return -1; |
| } |
| if (decode_parameter_from_string(priv,&str_ptr, "ASCII_CMD=", |
| PARAM_TYPE_STRING, sub_cmd, 6) != 0) { |
| return -1; |
| } |
| if (strncmp(sub_cmd, "AP_CFG", 6)) { |
| |
| if(!strncmp(sub_cmd ,"ADVCFG", 6)) { |
| return store_ap_advanced_config_from_string(priv, str_ptr); |
| } |
| unifi_error(priv,"store_ap_config_from_string: sub_cmd:%s != 'AP_CFG or ADVCFG'!\n", sub_cmd); |
| return -1; |
| } |
| memset(ap_config, 0, sizeof(CsrWifiSmeApConfig_t)); |
| ret = decode_parameter_from_string(priv,&str_ptr, "SSID=", |
| PARAM_TYPE_STRING, ap_config->ssid.ssid, |
| CSR_WIFI_MAX_SSID_LEN); |
| if(ret) { |
| unifi_error(priv,"store_ap_config_from_string: SSID not found\n"); |
| return -1; |
| } |
| ap_config->ssid.length = strlen(ap_config->ssid.ssid); |
| |
| ret = decode_parameter_from_string(priv, &str_ptr, "SEC=", |
| PARAM_TYPE_STRING, sec, CSR_WIFI_MAX_SEC_LEN); |
| if(ret) { |
| unifi_error(priv,"store_ap_config_from_string: SEC not found\n"); |
| return -1; |
| } |
| ret = decode_parameter_from_string(priv,&str_ptr, "KEY=", |
| PARAM_TYPE_STRING, key, CSR_WIFI_MAX_KEY_LEN); |
| if(!strcasecmp(sec,"open")) { |
| unifi_trace(priv,UDBG2,"store_ap_config_from_string: security open"); |
| ap_config->credentials.authType = CSR_WIFI_SME_AP_AUTH_TYPE_OPEN_SYSTEM; |
| if(ret) { |
| unifi_notice(priv,"store_ap_config_from_string: KEY not found:fine with Open\n"); |
| } |
| } |
| else if(!strcasecmp(sec,"wpa2-psk")) { |
| int i,j=0; |
| CsrWifiNmeApAuthPers *pers = |
| ((CsrWifiNmeApAuthPers *)&(ap_config->credentials.nmeAuthType.authTypePersonal)); |
| u8 *psk = pers->authPers_credentials.psk.psk; |
| |
| unifi_trace(priv,UDBG2,"store_ap_config_from_string: security WPA2"); |
| if(ret) { |
| unifi_error(priv,"store_ap_config_from_string: KEY not found for WPA2\n"); |
| return -1; |
| } |
| ap_config->credentials.authType = CSR_WIFI_SME_AP_AUTH_TYPE_PERSONAL; |
| pers->authSupport = CSR_WIFI_SME_RSN_AUTH_WPA2PSK; |
| pers->rsnCapabilities =0; |
| pers->wapiCapabilities =0; |
| pers->pskOrPassphrase=CSR_WIFI_NME_AP_CREDENTIAL_TYPE_PSK; |
| pers->authPers_credentials.psk.encryptionMode = |
| (CSR_WIFI_NME_ENCRYPTION_CIPHER_PAIRWISE_CCMP |CSR_WIFI_NME_ENCRYPTION_CIPHER_GROUP_CCMP) ; |
| for(i=0;i<32;i++){ |
| psk[i] = (16*hex_look_up(key[j]))+hex_look_up(key[j+1]); |
| j+=2; |
| } |
| |
| } else { |
| unifi_notice(priv,"store_ap_config_from_string: Unknown security: Assuming Open"); |
| ap_config->credentials.authType = CSR_WIFI_SME_AP_AUTH_TYPE_OPEN_SYSTEM; |
| return -1; |
| } |
| /* Get the decoded value in a temp int variable to ensure that other fields within the struct |
| which are of type other than int are not over written */ |
| ret = decode_parameter_from_string(priv,&str_ptr, "CHANNEL=", PARAM_TYPE_INT, &tmp_var, 5); |
| if(ret) |
| return -1; |
| ap_config->channel = tmp_var; |
| ret = decode_parameter_from_string(priv,&str_ptr, "PREAMBLE=", PARAM_TYPE_INT, &tmp_var, 5); |
| if(ret) |
| return -1; |
| ap_mac_config->preamble = tmp_var; |
| ret = decode_parameter_from_string(priv,&str_ptr, "MAX_SCB=", PARAM_TYPE_INT, &tmp_var, 5); |
| ap_config->max_connections = tmp_var; |
| return ret; |
| } |
| |
| static int |
| iwprivsapstart(struct net_device *dev, struct iw_request_info *info, |
| union iwreq_data *wrqu, char *extra) |
| { |
| netInterface_priv_t *interfacePriv = (netInterface_priv_t *)netdev_priv(dev); |
| unifi_priv_t *priv = interfacePriv->privPtr; |
| int r; |
| |
| unifi_trace(priv, UDBG1, "iwprivsapstart\n" ); |
| r = sme_ap_start(priv,interfacePriv->InterfaceTag,&priv->ap_config); |
| if(r) { |
| unifi_error(priv,"iwprivsapstart AP START failed : %d\n",-r); |
| } |
| return r; |
| } |
| |
| static int |
| iwprivsapconfig(struct net_device *dev, struct iw_request_info *info, |
| union iwreq_data *wrqu, char *extra) |
| { |
| netInterface_priv_t *interfacePriv = (netInterface_priv_t *)netdev_priv(dev); |
| unifi_priv_t *priv = interfacePriv->privPtr; |
| char *cfg_str = NULL; |
| int r; |
| |
| unifi_trace(priv, UDBG1, "iwprivsapconfig\n" ); |
| if (wrqu->data.length != 0) { |
| char *str; |
| if (!(cfg_str = kmalloc(wrqu->data.length+1, GFP_KERNEL))) |
| { |
| return -ENOMEM; |
| } |
| if (copy_from_user(cfg_str, wrqu->data.pointer, wrqu->data.length)) { |
| kfree(cfg_str); |
| return -EFAULT; |
| } |
| cfg_str[wrqu->data.length] = 0; |
| unifi_trace(priv,UDBG2,"length:%d\n",wrqu->data.length); |
| unifi_trace(priv,UDBG2,"AP configuration string:%s\n",cfg_str); |
| str = cfg_str; |
| if ((r = store_ap_config_from_string(priv,str))) { |
| unifi_error(priv, "iwprivsapconfig:Failed to decode the string %d\n",r); |
| kfree(cfg_str); |
| return -EIO; |
| |
| } |
| } else { |
| unifi_error(priv,"iwprivsapconfig argument length = 0 \n"); |
| return -EIO; |
| } |
| r = sme_ap_config(priv, &priv->ap_mac_config, &priv->group_sec_config); |
| if(r) { |
| unifi_error(priv,"iwprivsapstop AP Config failed : %d\n",-r); |
| } else if(interfacePriv->interfaceMode == CSR_WIFI_ROUTER_CTRL_MODE_AP || |
| interfacePriv->interfaceMode == CSR_WIFI_ROUTER_CTRL_MODE_P2PGO) { |
| unifi_trace(priv, UDBG1, "iwprivsapconfig: Starting the AP"); |
| r = sme_ap_start(priv,interfacePriv->InterfaceTag,&priv->ap_config); |
| if(r) { |
| unifi_error(priv,"iwprivsapstart AP START failed : %d\n",-r); |
| } |
| } |
| kfree(cfg_str); |
| return r; |
| } |
| |
| static int |
| iwprivsapstop(struct net_device *dev, struct iw_request_info *info, |
| union iwreq_data *wrqu, char *extra) |
| { |
| netInterface_priv_t *interfacePriv = (netInterface_priv_t *)netdev_priv(dev); |
| unifi_priv_t *priv = interfacePriv->privPtr; |
| int r; |
| u16 interface_tag = interfacePriv->InterfaceTag; |
| |
| unifi_trace(priv, UDBG1, "iwprivsapstop\n" ); |
| r = sme_ap_stop(priv,interface_tag); |
| if(r) { |
| unifi_error(priv,"iwprivsapstop AP STOP failed : %d\n",-r); |
| } |
| return r; |
| } |
| |
| #ifdef ANDROID_BUILD |
| static int |
| iwprivsapfwreload(struct net_device *dev, struct iw_request_info *info, |
| union iwreq_data *wrqu, char *extra) |
| { |
| netInterface_priv_t *interfacePriv = (netInterface_priv_t *)netdev_priv(dev); |
| unifi_priv_t *priv = interfacePriv->privPtr; |
| |
| unifi_trace(priv, UDBG1, "iwprivsapfwreload\n" ); |
| return 0; |
| } |
| |
| static int |
| iwprivsstackstart(struct net_device *dev, struct iw_request_info *info, |
| union iwreq_data *wrqu, char *extra) |
| { |
| netInterface_priv_t *interfacePriv = (netInterface_priv_t *)netdev_priv(dev); |
| unifi_priv_t *priv = interfacePriv->privPtr; |
| unifi_trace(priv, UDBG1, "iwprivsstackstart\n" ); |
| return 0; |
| } |
| |
| static int |
| iwprivsstackstop(struct net_device *dev, struct iw_request_info *info, |
| union iwreq_data *wrqu, char *extra) |
| { |
| netInterface_priv_t *interfacePriv = (netInterface_priv_t *)netdev_priv(dev); |
| unifi_priv_t *priv = interfacePriv->privPtr; |
| int r = 0; |
| u16 interface_tag = interfacePriv->InterfaceTag; |
| |
| unifi_trace(priv, UDBG1, "iwprivsstackstop\n" ); |
| |
| switch(interfacePriv->interfaceMode) { |
| case CSR_WIFI_ROUTER_CTRL_MODE_STA: |
| case CSR_WIFI_ROUTER_CTRL_MODE_P2PCLI: |
| case CSR_WIFI_ROUTER_CTRL_MODE_IBSS: |
| r = sme_mgt_disconnect(priv); |
| break; |
| case CSR_WIFI_ROUTER_CTRL_MODE_AP: |
| case CSR_WIFI_ROUTER_CTRL_MODE_P2PGO: |
| r = sme_ap_stop(priv,interface_tag); |
| break; |
| default : |
| break; |
| } |
| |
| if(r) { |
| unifi_error(priv,"iwprivsstackstop Stack stop failed : %d\n",-r); |
| } |
| return 0; |
| } |
| #endif /* ANDROID_BUILD */ |
| #endif /* CSR_SUPPORT_WEXT_AP */ |
| |
| #ifdef CSR_WIFI_SECURITY_WAPI_ENABLE |
| static int |
| iwprivsconfwapi(struct net_device *dev, struct iw_request_info *info, |
| union iwreq_data *wrqu, char *extra) |
| { |
| u8 enable; |
| netInterface_priv_t *interfacePriv = (netInterface_priv_t *)netdev_priv(dev); |
| unifi_priv_t *priv = interfacePriv->privPtr; |
| func_enter(); |
| |
| unifi_trace(priv, UDBG1, "iwprivsconfwapi\n" ); |
| |
| if(interfacePriv->interfaceMode == CSR_WIFI_ROUTER_CTRL_MODE_AP || |
| interfacePriv->interfaceMode == CSR_WIFI_ROUTER_CTRL_MODE_P2PGO) { |
| unifi_error(priv, "iwprivsconfwapi: not permitted in Mode %d\n", |
| interfacePriv->interfaceMode); |
| return -EPERM; |
| } |
| |
| enable = *(u8*)(extra); |
| |
| if (enable) { |
| priv->connection_config.authModeMask = CSR_WIFI_SME_AUTH_MODE_80211_OPEN; |
| priv->connection_config.authModeMask |= (CSR_WIFI_SME_AUTH_MODE_WAPI_WAIPSK | CSR_WIFI_SME_AUTH_MODE_WAPI_WAI); |
| priv->connection_config.encryptionModeMask |= |
| CSR_WIFI_SME_ENCRYPTION_CIPHER_PAIRWISE_SMS4 | CSR_WIFI_SME_ENCRYPTION_CIPHER_GROUP_SMS4; |
| } else { |
| priv->connection_config.authModeMask &= ~(CSR_WIFI_SME_AUTH_MODE_WAPI_WAIPSK | CSR_WIFI_SME_AUTH_MODE_WAPI_WAI); |
| priv->connection_config.encryptionModeMask &= |
| ~(CSR_WIFI_SME_ENCRYPTION_CIPHER_PAIRWISE_SMS4 | CSR_WIFI_SME_ENCRYPTION_CIPHER_GROUP_SMS4); |
| } |
| |
| func_exit(); |
| return 0; |
| } |
| |
| static int |
| iwprivswpikey(struct net_device *dev, struct iw_request_info *info, |
| union iwreq_data *wrqu, char *extra) |
| { |
| int r = 0, i; |
| CsrWifiSmeKey key; |
| unifiio_wapi_key_t inKey; |
| netInterface_priv_t *interfacePriv = (netInterface_priv_t *)netdev_priv(dev); |
| unifi_priv_t *priv = interfacePriv->privPtr; |
| func_enter(); |
| |
| unifi_trace(priv, UDBG1, "iwprivswpikey\n" ); |
| |
| if(interfacePriv->interfaceMode == CSR_WIFI_ROUTER_CTRL_MODE_AP || |
| interfacePriv->interfaceMode == CSR_WIFI_ROUTER_CTRL_MODE_P2PGO) { |
| unifi_error(priv, "iwprivswpikey: not permitted in Mode %d\n", |
| interfacePriv->interfaceMode); |
| return -EPERM; |
| } |
| |
| inKey = *(unifiio_wapi_key_t*)(extra); |
| |
| if (inKey.unicastKey) { |
| key.keyType = CSR_WIFI_SME_KEY_TYPE_PAIRWISE; |
| } else { |
| key.keyType = CSR_WIFI_SME_KEY_TYPE_GROUP; |
| } |
| |
| key.keyIndex = inKey.keyIndex; |
| |
| /* memcpy(key.keyRsc, inKey.keyRsc, 16); */ |
| for (i = 0; i < 16; i+= 2) |
| { |
| key.keyRsc[i/2] = inKey.keyRsc[i+1] << 8 | inKey.keyRsc[i]; |
| } |
| |
| memcpy(key.address.a, inKey.address, 6); |
| key.keyLength = 32; |
| memcpy(key.key, inKey.key, 32); |
| key.authenticator = 0; |
| key.wepTxKey = 0; |
| |
| unifi_trace(priv, UDBG1, "keyType = %d, keyIndex = %d, wepTxKey = %d, keyRsc = %x:%x, auth = %d, address = %x:%x, " |
| "keylength = %d, key = %x:%x\n", key.keyType, key.keyIndex, key.wepTxKey, |
| key.keyRsc[0], key.keyRsc[7], key.authenticator, |
| key.address.a[0], key.address.a[5], key.keyLength, key.key[0], |
| key.key[15]); |
| |
| r = sme_mgt_key(priv, &key, CSR_WIFI_SME_LIST_ACTION_ADD); |
| if (r) { |
| unifi_error(priv, "SETKEYS request was rejected with result %d\n", r); |
| return convert_sme_error(r); |
| } |
| |
| func_exit(); |
| return r; |
| } |
| #endif |
| |
| |
| static int |
| unifi_giwname(struct net_device *dev, struct iw_request_info *info, |
| union iwreq_data *wrqu, char *extra) |
| { |
| netInterface_priv_t *interfacePriv = (netInterface_priv_t *)netdev_priv(dev); |
| unifi_priv_t *priv = interfacePriv->privPtr; |
| char *name = wrqu->name; |
| unifi_trace(priv, UDBG2, "unifi_giwname\n"); |
| |
| if (priv->if_index == CSR_INDEX_5G) { |
| strcpy(name, "IEEE 802.11-a"); |
| } else { |
| strcpy(name, "IEEE 802.11-bgn"); |
| } |
| return 0; |
| } /* unifi_giwname() */ |
| |
| |
| static int |
| unifi_siwfreq(struct net_device *dev, struct iw_request_info *info, |
| union iwreq_data *wrqu, char *extra) |
| { |
| netInterface_priv_t *interfacePriv = (netInterface_priv_t *)netdev_priv(dev); |
| unifi_priv_t *priv = interfacePriv->privPtr; |
| struct iw_freq *freq = (struct iw_freq *)wrqu; |
| |
| func_enter(); |
| unifi_trace(priv, UDBG2, "unifi_siwfreq\n"); |
| |
| if(interfacePriv->interfaceMode == CSR_WIFI_ROUTER_CTRL_MODE_AP || |
| interfacePriv->interfaceMode == CSR_WIFI_ROUTER_CTRL_MODE_P2PGO) { |
| unifi_error(priv, "unifi_siwfreq: not permitted in Mode %d\n", |
| interfacePriv->interfaceMode); |
| return -EPERM; |
| } |
| |
| |
| /* |
| * Channel is stored in the connection configuration, |
| * and set later when ask for a connection. |
| */ |
| if ((freq->e == 0) && (freq->m <= 1000)) { |
| priv->connection_config.adhocChannel = freq->m; |
| } else { |
| priv->connection_config.adhocChannel = wext_freq_to_channel(freq->m, freq->e); |
| } |
| |
| func_exit(); |
| return 0; |
| } /* unifi_siwfreq() */ |
| |
| |
| static int |
| unifi_giwfreq(struct net_device *dev, struct iw_request_info *info, |
| union iwreq_data *wrqu, char *extra) |
| { |
| netInterface_priv_t *interfacePriv = (netInterface_priv_t *)netdev_priv(dev); |
| unifi_priv_t *priv = interfacePriv->privPtr; |
| struct iw_freq *freq = (struct iw_freq *)wrqu; |
| int err = 0; |
| CsrWifiSmeConnectionInfo connectionInfo; |
| |
| func_enter(); |
| unifi_trace(priv, UDBG2, "unifi_giwfreq\n"); |
| CHECK_INITED(priv); |
| |
| if(interfacePriv->interfaceMode == CSR_WIFI_ROUTER_CTRL_MODE_AP || |
| interfacePriv->interfaceMode == CSR_WIFI_ROUTER_CTRL_MODE_P2PGO) { |
| unifi_error(priv, "unifi_giwfreq: not permitted in Mode %d\n", |
| interfacePriv->interfaceMode); |
| return -EPERM; |
| } |
| |
| |
| UF_RTNL_UNLOCK(); |
| err = sme_mgt_connection_info_get(priv, &connectionInfo); |
| UF_RTNL_LOCK(); |
| |
| freq->m = channel_to_mhz(connectionInfo.channelNumber, |
| (connectionInfo.networkType80211 == CSR_WIFI_SME_RADIO_IF_GHZ_5_0)); |
| freq->e = 6; |
| |
| func_exit(); |
| return convert_sme_error(err); |
| } /* unifi_giwfreq() */ |
| |
| |
| static int |
| unifi_siwmode(struct net_device *dev, struct iw_request_info *info, |
| union iwreq_data *wrqu, char *extra) |
| { |
| netInterface_priv_t *interfacePriv = (netInterface_priv_t *)netdev_priv(dev); |
| unifi_priv_t *priv = interfacePriv->privPtr; |
| |
| func_enter(); |
| unifi_trace(priv, UDBG2, "unifi_siwmode\n"); |
| |
| if(interfacePriv->interfaceMode == CSR_WIFI_ROUTER_CTRL_MODE_AP || |
| interfacePriv->interfaceMode == CSR_WIFI_ROUTER_CTRL_MODE_P2PGO) { |
| unifi_error(priv, "unifi_siwmode: not permitted in Mode %d\n", |
| interfacePriv->interfaceMode); |
| return -EPERM; |
| } |
| |
| |
| switch(wrqu->mode) { |
| case IW_MODE_ADHOC: |
| priv->connection_config.bssType = CSR_WIFI_SME_BSS_TYPE_ADHOC; |
| break; |
| case IW_MODE_INFRA: |
| priv->connection_config.bssType = CSR_WIFI_SME_BSS_TYPE_INFRASTRUCTURE; |
| break; |
| case IW_MODE_AUTO: |
| priv->connection_config.bssType = CSR_WIFI_SME_BSS_TYPE_ANY_BSS; |
| break; |
| default: |
| unifi_notice(priv, "Unknown IW MODE value.\n"); |
| } |
| |
| /* Clear the SSID and BSSID configuration */ |
| priv->connection_config.ssid.length = 0; |
| memset(priv->connection_config.bssid.a, 0xFF, ETH_ALEN); |
| |
| func_exit(); |
| return 0; |
| } /* unifi_siwmode() */ |
| |
| |
| |
| static int |
| unifi_giwmode(struct net_device *dev, struct iw_request_info *info, |
| union iwreq_data *wrqu, char *extra) |
| { |
| int r = 0; |
| netInterface_priv_t *interfacePriv = (netInterface_priv_t *)netdev_priv(dev); |
| unifi_priv_t *priv = interfacePriv->privPtr; |
| CsrWifiSmeConnectionConfig connectionConfig; |
| |
| func_enter(); |
| unifi_trace(priv, UDBG2, "unifi_giwmode\n"); |
| CHECK_INITED(priv); |
| |
| unifi_trace(priv, UDBG2, "unifi_giwmode: Exisitng mode = 0x%x\n", |
| interfacePriv->interfaceMode); |
| switch(interfacePriv->interfaceMode) { |
| case CSR_WIFI_ROUTER_CTRL_MODE_STA: |
| case CSR_WIFI_ROUTER_CTRL_MODE_P2PCLI: |
| wrqu->mode = IW_MODE_INFRA; |
| break; |
| case CSR_WIFI_ROUTER_CTRL_MODE_AP: |
| case CSR_WIFI_ROUTER_CTRL_MODE_P2PGO: |
| wrqu->mode = IW_MODE_MASTER; |
| break; |
| case CSR_WIFI_ROUTER_CTRL_MODE_IBSS: |
| wrqu->mode = IW_MODE_ADHOC; |
| break; |
| case CSR_WIFI_ROUTER_CTRL_MODE_P2P: |
| case CSR_WIFI_ROUTER_CTRL_MODE_NONE: |
| UF_RTNL_UNLOCK(); |
| r = sme_mgt_connection_config_get(priv, &connectionConfig); |
| UF_RTNL_LOCK(); |
| if (r == 0) { |
| switch(connectionConfig.bssType) { |
| case CSR_WIFI_SME_BSS_TYPE_ADHOC: |
| wrqu->mode = IW_MODE_ADHOC; |
| break; |
| case CSR_WIFI_SME_BSS_TYPE_INFRASTRUCTURE: |
| wrqu->mode = IW_MODE_INFRA; |
| break; |
| default: |
| wrqu->mode = IW_MODE_AUTO; |
| unifi_notice(priv, "Unknown IW MODE value.\n"); |
| } |
| } |
| break; |
| default: |
| wrqu->mode = IW_MODE_AUTO; |
| unifi_notice(priv, "Unknown IW MODE value.\n"); |
| |
| } |
| unifi_trace(priv, UDBG4, "unifi_giwmode: mode = 0x%x\n", wrqu->mode); |
| func_exit(); |
| return r; |
| } /* unifi_giwmode() */ |
| |
| |
| |
| static int |
| unifi_giwrange(struct net_device *dev, struct iw_request_info *info, |
| union iwreq_data *wrqu, char *extra) |
| { |
| struct iw_point *dwrq = &wrqu->data; |
| struct iw_range *range = (struct iw_range *) extra; |
| int i; |
| |
| unifi_trace(NULL, UDBG2, "unifi_giwrange\n"); |
| |
| dwrq->length = sizeof(struct iw_range); |
| memset(range, 0, sizeof(*range)); |
| range->min_nwid = 0x0000; |
| range->max_nwid = 0x0000; |
| |
| /* |
| * Don't report the frequency/channel table, then the channel |
| * number returned elsewhere will be printed as a channel number. |
| */ |
| |
| /* Ranges of values reported in quality structs */ |
| range->max_qual.qual = 40; /* Max expected qual value */ |
| range->max_qual.level = -120; /* Noise floor in dBm */ |
| range->max_qual.noise = -120; /* Noise floor in dBm */ |
| |
| |
| /* space for IW_MAX_BITRATES (8 up to WE15, 32 later) */ |
| i = 0; |
| #if WIRELESS_EXT > 15 |
| range->bitrate[i++] = 2 * 500000; |
| range->bitrate[i++] = 4 * 500000; |
| range->bitrate[i++] = 11 * 500000; |
| range->bitrate[i++] = 22 * 500000; |
| range->bitrate[i++] = 12 * 500000; |
| range->bitrate[i++] = 18 * 500000; |
| range->bitrate[i++] = 24 * 500000; |
| range->bitrate[i++] = 36 * 500000; |
| range->bitrate[i++] = 48 * 500000; |
| range->bitrate[i++] = 72 * 500000; |
| range->bitrate[i++] = 96 * 500000; |
| range->bitrate[i++] = 108 * 500000; |
| #else |
| range->bitrate[i++] = 2 * 500000; |
| range->bitrate[i++] = 4 * 500000; |
| range->bitrate[i++] = 11 * 500000; |
| range->bitrate[i++] = 22 * 500000; |
| range->bitrate[i++] = 24 * 500000; |
| range->bitrate[i++] = 48 * 500000; |
| range->bitrate[i++] = 96 * 500000; |
| range->bitrate[i++] = 108 * 500000; |
| #endif /* WIRELESS_EXT < 16 */ |
| range->num_bitrates = i; |
| |
| range->max_encoding_tokens = NUM_WEPKEYS; |
| range->num_encoding_sizes = 2; |
| range->encoding_size[0] = 5; |
| range->encoding_size[1] = 13; |
| |
| range->we_version_source = 20; |
| range->we_version_compiled = WIRELESS_EXT; |
| |
| /* Number of channels available in h/w */ |
| range->num_channels = 14; |
| /* Number of entries in freq[] array */ |
| range->num_frequency = 14; |
| for (i = 0; (i < range->num_frequency) && (i < IW_MAX_FREQUENCIES); i++) { |
| int chan = i + 1; |
| range->freq[i].i = chan; |
| range->freq[i].m = channel_to_mhz(chan, 0); |
| range->freq[i].e = 6; |
| } |
| if ((i+3) < IW_MAX_FREQUENCIES) { |
| range->freq[i].i = 36; |
| range->freq[i].m = channel_to_mhz(36, 1); |
| range->freq[i].e = 6; |
| range->freq[i+1].i = 40; |
| range->freq[i+1].m = channel_to_mhz(40, 1); |
| range->freq[i+1].e = 6; |
| range->freq[i+2].i = 44; |
| range->freq[i+2].m = channel_to_mhz(44, 1); |
| range->freq[i+2].e = 6; |
| range->freq[i+3].i = 48; |
| range->freq[i+3].m = channel_to_mhz(48, 1); |
| range->freq[i+3].e = 6; |
| } |
| |
| #if WIRELESS_EXT > 16 |
| /* Event capability (kernel + driver) */ |
| range->event_capa[0] = (IW_EVENT_CAPA_K_0 | |
| IW_EVENT_CAPA_MASK(SIOCGIWTHRSPY) | |
| 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(IWEVTXDROP) | |
| IW_EVENT_CAPA_MASK(IWEVCUSTOM) | |
| IW_EVENT_CAPA_MASK(IWEVREGISTERED) | |
| IW_EVENT_CAPA_MASK(IWEVEXPIRED)); |
| #endif /* WIRELESS_EXT > 16 */ |
| |
| #if WIRELESS_EXT > 17 |
| range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_WPA2 | |
| IW_ENC_CAPA_CIPHER_TKIP | IW_ENC_CAPA_CIPHER_CCMP; |
| #endif /* WIRELESS_EXT > 17 */ |
| |
| |
| return 0; |
| } /* unifi_giwrange() */ |
| |
| |
| static int |
| unifi_siwap(struct net_device *dev, struct iw_request_info *info, |
| union iwreq_data *wrqu, char *extra) |
| { |
| netInterface_priv_t *interfacePriv = (netInterface_priv_t *)netdev_priv(dev); |
| unifi_priv_t *priv = interfacePriv->privPtr; |
| int err = 0; |
| |
| func_enter(); |
| |
| CHECK_INITED(priv); |
| |
| if(interfacePriv->interfaceMode == CSR_WIFI_ROUTER_CTRL_MODE_AP || |
| interfacePriv->interfaceMode == CSR_WIFI_ROUTER_CTRL_MODE_P2PGO) { |
| unifi_error(priv, "unifi_siwap: not permitted in Mode %d\n", |
| interfacePriv->interfaceMode); |
| return -EPERM; |
| } |
| |
| |
| if (wrqu->ap_addr.sa_family != ARPHRD_ETHER) { |
| return -EINVAL; |
| } |
| |
| unifi_trace(priv, UDBG1, "unifi_siwap: asked for %pM\n", |
| wrqu->ap_addr.sa_data); |
| |
| if (is_zero_ether_addr(wrqu->ap_addr.sa_data)) { |
| priv->ignore_bssid_join = FALSE; |
| err = sme_mgt_disconnect(priv); |
| if (err) { |
| unifi_trace(priv, UDBG4, "unifi_siwap: Disconnect failed, status %d\n", err); |
| } |
| return 0; |
| } |
| |
| if (priv->ignore_bssid_join) { |
| unifi_trace(priv, UDBG4, "unifi_siwap: ignoring second join\n"); |
| priv->ignore_bssid_join = FALSE; |
| } else { |
| memcpy(priv->connection_config.bssid.a, wrqu->ap_addr.sa_data, ETH_ALEN); |
| unifi_trace(priv, UDBG1, "unifi_siwap: Joining %X:%X:%X:%X:%X:%X\n", |
| priv->connection_config.bssid.a[0], |
| priv->connection_config.bssid.a[1], |
| priv->connection_config.bssid.a[2], |
| priv->connection_config.bssid.a[3], |
| priv->connection_config.bssid.a[4], |
| priv->connection_config.bssid.a[5]); |
| err = sme_mgt_connect(priv); |
| if (err) { |
| unifi_error(priv, "unifi_siwap: Join failed, status %d\n", err); |
| func_exit(); |
| return convert_sme_error(err); |
| } |
| } |
| func_exit(); |
| |
| return 0; |
| } /* unifi_siwap() */ |
| |
| |
| static int |
| unifi_giwap(struct net_device *dev, struct iw_request_info *info, |
| union iwreq_data *wrqu, char *extra) |
| { |
| netInterface_priv_t *interfacePriv = (netInterface_priv_t *)netdev_priv(dev); |
| unifi_priv_t *priv = interfacePriv->privPtr; |
| CsrWifiSmeConnectionInfo connectionInfo; |
| int r = 0; |
| u8 *bssid; |
| |
| func_enter(); |
| |
| CHECK_INITED(priv); |
| unifi_trace(priv, UDBG2, "unifi_giwap\n"); |
| |
| if(interfacePriv->interfaceMode == CSR_WIFI_ROUTER_CTRL_MODE_AP || |
| interfacePriv->interfaceMode == CSR_WIFI_ROUTER_CTRL_MODE_P2PGO) { |
| unifi_error(priv, "iwprivswpikey: not permitted in Mode %d\n", |
| interfacePriv->interfaceMode); |
| return -EPERM; |
| } |
| |
| UF_RTNL_UNLOCK(); |
| r = sme_mgt_connection_info_get(priv, &connectionInfo); |
| UF_RTNL_LOCK(); |
| |
| if (r == 0) { |
| bssid = connectionInfo.bssid.a; |
| wrqu->ap_addr.sa_family = ARPHRD_ETHER; |
| unifi_trace(priv, UDBG4, "unifi_giwap: BSSID = %pM\n", bssid); |
| |
| memcpy(wrqu->ap_addr.sa_data, bssid, ETH_ALEN); |
| } else { |
| memset(wrqu->ap_addr.sa_data, 0, ETH_ALEN); |
| } |
| |
| func_exit(); |
| return 0; |
| } /* unifi_giwap() */ |
| |
| |
| static int |
| unifi_siwscan(struct net_device *dev, struct iw_request_info *info, |
| union iwreq_data *wrqu, char *extra) |
| { |
| netInterface_priv_t *interfacePriv = (netInterface_priv_t *)netdev_priv(dev); |
| unifi_priv_t *priv = interfacePriv->privPtr; |
| int scantype; |
| int r; |
| CsrWifiSsid scan_ssid; |
| unsigned char *channel_list = NULL; |
| int chans_good = 0; |
| #if WIRELESS_EXT > 17 |
| struct iw_point *data = &wrqu->data; |
| struct iw_scan_req *req = (struct iw_scan_req *) extra; |
| #endif |
| |
| func_enter(); |
| |
| CHECK_INITED(priv); |
| |
| if(interfacePriv->interfaceMode == CSR_WIFI_ROUTER_CTRL_MODE_AP || |
| interfacePriv->interfaceMode == CSR_WIFI_ROUTER_CTRL_MODE_P2PGO) { |
| unifi_error(priv, "unifi_siwscan: not permitted in Mode %d\n", |
| interfacePriv->interfaceMode); |
| return -EPERM; |
| } |
| |
| |
| scantype = UNIFI_SCAN_ACTIVE; |
| |
| #if WIRELESS_EXT > 17 |
| /* Providing a valid channel list will force an active scan */ |
| if (req) { |
| if ((req->num_channels > 0) && (req->num_channels < IW_MAX_FREQUENCIES)) { |
| channel_list = kmalloc(req->num_channels, GFP_KERNEL); |
| if (channel_list) { |
| int i; |
| for (i = 0; i < req->num_channels; i++) { |
| /* Convert frequency to channel number */ |
| int ch = wext_freq_to_channel(req->channel_list[i].m, |
| req->channel_list[i].e); |
| if (ch) { |
| channel_list[chans_good++] = ch; |
| } |
| } |
| unifi_trace(priv, UDBG1, |
| "SIWSCAN: Scanning %d channels\n", chans_good); |
| } else { |
| /* Fall back to scanning all */ |
| unifi_error(priv, "SIWSCAN: Can't alloc channel_list (%d)\n", |
| req->num_channels); |
| } |
| } |
| } |
| |
| if (req && (data->flags & IW_SCAN_THIS_ESSID)) { |
| memcpy(scan_ssid.ssid, req->essid, req->essid_len); |
| scan_ssid.length = req->essid_len; |
| unifi_trace(priv, UDBG1, |
| "SIWSCAN: Scanning for %.*s\n", |
| scan_ssid.length, scan_ssid.ssid); |
| } else |
| #endif |
| { |
| unifi_trace(priv, UDBG1, "SIWSCAN: Scanning for all APs\n"); |
| scan_ssid.length = 0; |
| } |
| |
| r = sme_mgt_scan_full(priv, &scan_ssid, chans_good, channel_list); |
| if (r) { |
| unifi_error(priv, "SIWSCAN: Scan returned error %d\n", r); |
| } else { |
| unifi_trace(priv, UDBG1, "SIWSCAN: Scan done\n"); |
| wext_send_scan_results_event(priv); |
| } |
| |
| if (channel_list) { |
| kfree(channel_list); |
| } |
| |
| func_exit(); |
| return r; |
| |
| } /* unifi_siwscan() */ |
| |
| |
| static const unsigned char * |
| unifi_find_info_element(int id, const unsigned char *info, int len) |
| { |
| const unsigned char *ie = info; |
| |
| while (len > 1) |
| { |
| int e_id, e_len; |
| e_id = ie[0]; |
| e_len = ie[1]; |
| |
| /* Return if we find a match */ |
| if (e_id == id) |
| { |
| return ie; |
| } |
| |
| len -= (e_len + 2); |
| ie += (e_len + 2); |
| } |
| |
| return NULL; |
| } /* unifi_find_info_element() */ |
| |
| |
| /* |
| * Translate scan data returned from the card to a card independent |
| * format that the Wireless Tools will understand - Jean II |
| */ |
| int |
| unifi_translate_scan(struct net_device *dev, |
| struct iw_request_info *info, |
| char *current_ev, char *end_buf, |
| CsrWifiSmeScanResult *scan_data, |
| int scan_index) |
| { |
| struct iw_event iwe; /* Temporary buffer */ |
| unsigned char *info_elems; |
| int info_elem_len; |
| const unsigned char *elem; |
| u16 capabilities; |
| int signal, noise, snr; |
| char *start_buf = current_ev; |
| char *current_val; /* For rates */ |
| int i, r; |
| |
| info_elems = scan_data->informationElements; |
| info_elem_len = scan_data->informationElementsLength; |
| |
| if (!scan_data->informationElementsLength || !scan_data->informationElements) { |
| unifi_error(NULL, "*** NULL SCAN IEs ***\n"); |
| return -EIO; |
| } |
| |
| /* get capinfo bits */ |
| capabilities = scan_data->capabilityInformation; |
| |
| unifi_trace(NULL, UDBG5, "Capabilities: 0x%x\n", capabilities); |
| |
| /* First entry *MUST* be the AP MAC address */ |
| memset(&iwe, 0, sizeof(iwe)); |
| iwe.cmd = SIOCGIWAP; |
| iwe.u.ap_addr.sa_family = ARPHRD_ETHER; |
| memcpy(iwe.u.ap_addr.sa_data, scan_data->bssid.a, ETH_ALEN); |
| iwe.len = IW_EV_ADDR_LEN; |
| r = uf_iwe_stream_add_event(info, start_buf, end_buf, &iwe, IW_EV_ADDR_LEN); |
| if (r < 0) { |
| return r; |
| } |
| start_buf += r; |
| |
| /* Other entries will be displayed in the order we give them */ |
| |
| /* Add the ESSID */ |
| /* find SSID in Info Elems */ |
| elem = unifi_find_info_element(IE_SSID_ID, info_elems, info_elem_len); |
| if (elem) { |
| int e_len = elem[1]; |
| const unsigned char *e_ptr = elem + 2; |
| unsigned char buf[33]; |
| |
| memset(&iwe, 0, sizeof(iwe)); |
| iwe.cmd = SIOCGIWESSID; |
| iwe.u.essid.length = e_len; |
| if (iwe.u.essid.length > 32) { |
| iwe.u.essid.length = 32; |
| } |
| iwe.u.essid.flags = scan_index; |
| memcpy(buf, e_ptr, iwe.u.essid.length); |
| buf[iwe.u.essid.length] = '\0'; |
| r = uf_iwe_stream_add_point(info, start_buf, end_buf, &iwe, buf); |
| if (r < 0) { |
| return r; |
| } |
| start_buf += r; |
| |
| } |
| |
| /* Add mode */ |
| memset(&iwe, 0, sizeof(iwe)); |
| iwe.cmd = SIOCGIWMODE; |
| if (scan_data->bssType == CSR_WIFI_SME_BSS_TYPE_INFRASTRUCTURE) { |
| iwe.u.mode = IW_MODE_INFRA; |
| } else { |
| iwe.u.mode = IW_MODE_ADHOC; |
| } |
| iwe.len = IW_EV_UINT_LEN; |
| r = uf_iwe_stream_add_event(info, start_buf, end_buf, &iwe, IW_EV_UINT_LEN); |
| if (r < 0) { |
| return r; |
| } |
| start_buf += r; |
| |
| /* Add frequency. iwlist will convert to channel using table given in giwrange */ |
| memset(&iwe, 0, sizeof(iwe)); |
| iwe.cmd = SIOCGIWFREQ; |
| iwe.u.freq.m = scan_data->channelFrequency; |
| iwe.u.freq.e = 6; |
| r = uf_iwe_stream_add_event(info, start_buf, end_buf, &iwe, IW_EV_FREQ_LEN); |
| if (r < 0) { |
| return r; |
| } |
| start_buf += r; |
| |
| |
| /* Add quality statistics */ |
| iwe.cmd = IWEVQUAL; |
| /* |
| * level and noise below are mapped into an unsigned 8 bit number, |
| * ranging from [-192; 63]. The way this is achieved is simply to |
| * add 0x100 onto the number if it is negative, |
| * once clipped to the correct range. |
| */ |
| signal = scan_data->rssi; /* This value is in dBm */ |
| /* Clip range of snr */ |
| snr = (scan_data->snr > 0) ? scan_data->snr : 0; /* In dB relative, from 0 - 255 */ |
| snr = (snr < 255) ? snr : 255; |
| noise = signal - snr; |
| |
| /* Clip range of signal */ |
| signal = (signal < 63) ? signal : 63; |
| signal = (signal > -192) ? signal : -192; |
| |
| /* Clip range of noise */ |
| noise = (noise < 63) ? noise : 63; |
| noise = (noise > -192) ? noise : -192; |
| |
| /* Make u8 */ |
| signal = ( signal < 0 ) ? signal + 0x100 : signal; |
| noise = ( noise < 0 ) ? noise + 0x100 : noise; |
| |
| iwe.u.qual.level = (u8)signal; /* -192 : 63 */ |
| iwe.u.qual.noise = (u8)noise; /* -192 : 63 */ |
| iwe.u.qual.qual = snr; /* 0 : 255 */ |
| iwe.u.qual.updated = 0; |
| #if WIRELESS_EXT > 16 |
| iwe.u.qual.updated |= IW_QUAL_LEVEL_UPDATED | IW_QUAL_NOISE_UPDATED | |
| IW_QUAL_QUAL_UPDATED; |
| #if WIRELESS_EXT > 18 |
| iwe.u.qual.updated |= IW_QUAL_DBM; |
| #endif |
| #endif |
| r = uf_iwe_stream_add_event(info, start_buf, end_buf, &iwe, IW_EV_QUAL_LEN); |
| if (r < 0) { |
| return r; |
| } |
| start_buf += r; |
| |
| /* Add encryption capability */ |
| iwe.cmd = SIOCGIWENCODE; |
| if (capabilities & SIG_CAP_PRIVACY) { |
| iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; |
| } else { |
| iwe.u.data.flags = IW_ENCODE_DISABLED; |
| } |
| iwe.u.data.length = 0; |
| iwe.len = IW_EV_POINT_LEN + iwe.u.data.length; |
| r = uf_iwe_stream_add_point(info, start_buf, end_buf, &iwe, ""); |
| if (r < 0) { |
| return r; |
| } |
| start_buf += r; |
| |
| |
| /* |
| * Rate : stuffing multiple values in a single event require a bit |
| * more of magic - Jean II |
| */ |
| current_val = start_buf + IW_EV_LCP_LEN; |
| |
| iwe.cmd = SIOCGIWRATE; |
| /* Those two flags are ignored... */ |
| iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0; |
| |
| elem = unifi_find_info_element(IE_SUPPORTED_RATES_ID, |
| info_elems, info_elem_len); |
| if (elem) { |
| int e_len = elem[1]; |
| const unsigned char *e_ptr = elem + 2; |
| |
| /* |
| * Count how many rates we have. |
| * Zero marks the end of the list, if the list is not truncated. |
| */ |
| /* Max 8 values */ |
| for (i = 0; i < e_len; i++) { |
| if (e_ptr[i] == 0) { |
| break; |
| } |
| /* Bit rate given in 500 kb/s units (+ 0x80) */ |
| iwe.u.bitrate.value = ((e_ptr[i] & 0x7f) * 500000); |
| /* Add new value to event */ |
| r = uf_iwe_stream_add_value(info, start_buf, current_val, end_buf, &iwe, IW_EV_PARAM_LEN); |
| if (r < 0) { |
| return r; |
| } |
| current_val +=r; |
| |
| } |
| } |
| elem = unifi_find_info_element(IE_EXTENDED_SUPPORTED_RATES_ID, |
| info_elems, info_elem_len); |
| if (elem) { |
| int e_len = elem[1]; |
| const unsigned char *e_ptr = elem + 2; |
| |
| /* |
| * Count how many rates we have. |
| * Zero marks the end of the list, if the list is not truncated. |
| */ |
| /* Max 8 values */ |
| for (i = 0; i < e_len; i++) { |
| if (e_ptr[i] == 0) { |
| break; |
| } |
| /* Bit rate given in 500 kb/s units (+ 0x80) */ |
| iwe.u.bitrate.value = ((e_ptr[i] & 0x7f) * 500000); |
| /* Add new value to event */ |
| r = uf_iwe_stream_add_value(info, start_buf, current_val, end_buf, &iwe, IW_EV_PARAM_LEN); |
| if (r < 0) { |
| return r; |
| } |
| current_val +=r; |
| } |
| } |
| /* Check if we added any rates event */ |
| if ((current_val - start_buf) > IW_EV_LCP_LEN) { |
| start_buf = current_val; |
| } |
| |
| |
| #if WIRELESS_EXT > 17 |
| memset(&iwe, 0, sizeof(iwe)); |
| iwe.cmd = IWEVGENIE; |
| iwe.u.data.length = info_elem_len; |
| |
| r = uf_iwe_stream_add_point(info, start_buf, end_buf, &iwe, info_elems); |
| if (r < 0) { |
| return r; |
| } |
| |
| start_buf += r; |
| #endif /* WE > 17 */ |
| |
| return (start_buf - current_ev); |
| } /* unifi_translate_scan() */ |
| |
| |
| |
| static int |
| unifi_giwscan(struct net_device *dev, struct iw_request_info *info, |
| union iwreq_data *wrqu, char *extra) |
| { |
| netInterface_priv_t *interfacePriv = (netInterface_priv_t *)netdev_priv(dev); |
| unifi_priv_t *priv = interfacePriv->privPtr; |
| struct iw_point *dwrq = &wrqu->data; |
| int r; |
| |
| CHECK_INITED(priv); |
| |
| if(interfacePriv->interfaceMode == CSR_WIFI_ROUTER_CTRL_MODE_AP || |
| interfacePriv->interfaceMode == CSR_WIFI_ROUTER_CTRL_MODE_P2PGO) { |
| unifi_error(priv, "unifi_giwscan: not permitted in Mode %d\n", |
| interfacePriv->interfaceMode); |
| return -EPERM; |
| } |
| |
| |
| unifi_trace(priv, UDBG1, |
| "unifi_giwscan: buffer (%d bytes) \n", |
| dwrq->length); |
| UF_RTNL_UNLOCK(); |
| r = sme_mgt_scan_results_get_async(priv, info, extra, dwrq->length); |
| UF_RTNL_LOCK(); |
| if (r < 0) { |
| unifi_trace(priv, UDBG1, |
| "unifi_giwscan: buffer (%d bytes) not big enough.\n", |
| dwrq->length); |
| return r; |
| } |
| |
| dwrq->length = r; |
| dwrq->flags = 0; |
| |
| return 0; |
| } /* unifi_giwscan() */ |
| |
| |
| /* |
| * --------------------------------------------------------------------------- |
| * unifi_siwessid |
| * |
| * Request to join a network or start and AdHoc. |
| * |
| * Arguments: |
| * dev Pointer to network device struct. |
| * info Pointer to broken-out ioctl request. |
| * data Pointer to argument data. |
| * essid Pointer to string giving name of network to join |
| * or start |
| * |
| * Returns: |
| * 0 on success and everything complete |
| * -EINPROGRESS to have the higher level call the commit method. |
| * --------------------------------------------------------------------------- |
| */ |
| static int |
| unifi_siwessid(struct net_device *dev, struct iw_request_info *info, |
| struct iw_point *data, char *essid) |
| { |
| netInterface_priv_t *interfacePriv = (netInterface_priv_t *)netdev_priv(dev); |
| unifi_priv_t *priv = interfacePriv->privPtr; |
| int len; |
| int err = 0; |
| |
| func_enter(); |
| CHECK_INITED(priv); |
| |
| if(interfacePriv->interfaceMode == CSR_WIFI_ROUTER_CTRL_MODE_AP || |
| interfacePriv->interfaceMode == CSR_WIFI_ROUTER_CTRL_MODE_P2PGO) { |
| unifi_error(priv, "unifi_siwessid: not permitted in Mode %d\n", |
| interfacePriv->interfaceMode); |
| return -EPERM; |
| } |
| |
| |
| len = 0; |
| if (data->flags & 1) { |
| /* Limit length */ |
| len = data->length; |
| if (len > UNIFI_MAX_SSID_LEN) { |
| len = UNIFI_MAX_SSID_LEN; |
| } |
| } |
| |
| #ifdef UNIFI_DEBUG |
| { |
| char essid_str[UNIFI_MAX_SSID_LEN+1]; |
| int i; |
| |
| for (i = 0; i < len; i++) { |
| essid_str[i] = (isprint(essid[i]) ? essid[i] : '?'); |
| } |
| essid_str[i] = '\0'; |
| |
| unifi_trace(priv, UDBG1, "unifi_siwessid: asked for '%*s' (%d)\n", len, essid_str, len); |
| unifi_trace(priv, UDBG2, " with authModeMask = %d", priv->connection_config.authModeMask); |
| } |
| #endif |
| |
| memset(priv->connection_config.bssid.a, 0xFF, ETH_ALEN); |
| if (len) { |
| if (essid[len - 1] == 0) { |
| len --; |
| } |
| |
| memcpy(priv->connection_config.ssid.ssid, essid, len); |
| priv->connection_config.ssid.length = len; |
| |
| } else { |
| priv->connection_config.ssid.length = 0; |
| } |
| |
| UF_RTNL_UNLOCK(); |
| err = sme_mgt_connect(priv); |
| UF_RTNL_LOCK(); |
| if (err) { |
| unifi_error(priv, "unifi_siwessid: Join failed, status %d\n", err); |
| func_exit(); |
| return convert_sme_error(err); |
| } |
| |
| func_exit(); |
| return 0; |
| } /* unifi_siwessid() */ |
| |
| |
| static int |
| unifi_giwessid(struct net_device *dev, struct iw_request_info *info, |
| union iwreq_data *wrqu, char *essid) |
| { |
| netInterface_priv_t *interfacePriv = (netInterface_priv_t *)netdev_priv(dev); |
| unifi_priv_t *priv = interfacePriv->privPtr; |
| struct iw_point *data = &wrqu->essid; |
| CsrWifiSmeConnectionInfo connectionInfo; |
| int r = 0; |
| |
| func_enter(); |
| unifi_trace(priv, UDBG2, "unifi_giwessid\n"); |
| CHECK_INITED(priv); |
| |
| if(interfacePriv->interfaceMode == CSR_WIFI_ROUTER_CTRL_MODE_AP || |
| interfacePriv->interfaceMode == CSR_WIFI_ROUTER_CTRL_MODE_P2PGO) { |
| unifi_error(priv, "unifi_giwessid: not permitted in Mode %d\n", |
| interfacePriv->interfaceMode); |
| return -EPERM; |
| } |
| |
| UF_RTNL_UNLOCK(); |
| r = sme_mgt_connection_info_get(priv, &connectionInfo); |
| UF_RTNL_LOCK(); |
| |
| if (r == 0) { |
| data->length = connectionInfo.ssid.length; |
| strncpy(essid, |
| connectionInfo.ssid.ssid, |
| data->length); |
| data->flags = 1; /* active */ |
| |
| unifi_trace(priv, UDBG2, "unifi_giwessid: %.*s\n", |
| data->length, essid); |
| } |
| |
| func_exit(); |
| |
| return 0; |
| } /* unifi_giwessid() */ |
| |
| |
| static int |
| unifi_siwrate(struct net_device *dev, struct iw_request_info *info, |
| union iwreq_data *wrqu, char *extra) |
| { |
| netInterface_priv_t *interfacePriv = (netInterface_priv_t *)netdev_priv(dev); |
| unifi_priv_t *priv = interfacePriv->privPtr; |
| struct iw_param *args = &wrqu->bitrate; |
| CsrWifiSmeMibConfig mibConfig; |
| int r; |
| |
| func_enter(); |
| |
| CHECK_INITED(priv); |
| unifi_trace(priv, UDBG2, "unifi_siwrate\n"); |
| |
| /* |
| * If args->fixed == 0, value is max rate or -1 for best |
| * If args->fixed == 1, value is rate to set or -1 for best |
| * args->disabled and args->flags are not used in SIOCSIWRATE |
| */ |
| |
| /* Get, modify and set the MIB data */ |
| UF_RTNL_UNLOCK(); |
| r = sme_mgt_mib_config_get(priv, &mibConfig); |
| UF_RTNL_LOCK(); |
| if (r) { |
| unifi_error(priv, "unifi_siwrate: Get CsrWifiSmeMibConfigValue failed.\n"); |
| return r; |
| } |
| |
| /* Default to auto rate algorithm */ |
| /* in 500Kbit/s, 0 means auto */ |
| mibConfig.unifiFixTxDataRate = 0; |
| |
| if (args->value != -1) { |
| mibConfig.unifiFixTxDataRate = args->value / 500000; |
| } |
| |
| /* 1 means rate is a maximum, 2 means rate is a set value */ |
| if (args->fixed == 1) { |
| mibConfig.unifiFixMaxTxDataRate = 0; |
| } else { |
| mibConfig.unifiFixMaxTxDataRate = 1; |
| } |
| UF_RTNL_UNLOCK(); |
| r = sme_mgt_mib_config_set(priv, &mibConfig); |
| UF_RTNL_LOCK(); |
| if (r) { |
| unifi_error(priv, "unifi_siwrate: Set CsrWifiSmeMibConfigValue failed.\n"); |
| return r; |
| } |
| |
| func_exit(); |
| |
| return 0; |
| } /* unifi_siwrate() */ |
| |
| |
| |
| static int |
| unifi_giwrate(struct net_device *dev, struct iw_request_info *info, |
| union iwreq_data *wrqu, char *extra) |
| { |
| netInterface_priv_t *interfacePriv = (netInterface_priv_t *)netdev_priv(dev); |
| unifi_priv_t *priv = interfacePriv->privPtr; |
| struct iw_param *args = &wrqu->bitrate; |
| int r; |
| int bitrate, flag; |
| CsrWifiSmeMibConfig mibConfig; |
| CsrWifiSmeConnectionStats connectionStats; |
| |
| func_enter(); |
| unifi_trace(priv, UDBG2, "unifi_giwrate\n"); |
| CHECK_INITED(priv); |
| |
| flag = 0; |
| bitrate = 0; |
| UF_RTNL_UNLOCK(); |
| r = sme_mgt_mib_config_get(priv, &mibConfig); |
| UF_RTNL_LOCK(); |
| if (r) { |
| unifi_error(priv, "unifi_giwrate: Get CsrWifiSmeMibConfigValue failed.\n"); |
| return r; |
| } |
| |
| bitrate = mibConfig.unifiFixTxDataRate; |
| flag = mibConfig.unifiFixMaxTxDataRate; |
| |
| /* Used the value returned by the SME if MIB returns 0 */ |
| if (bitrate == 0) { |
| UF_RTNL_UNLOCK(); |
| r = sme_mgt_connection_stats_get(priv, &connectionStats); |
| UF_RTNL_LOCK(); |
| /* Ignore errors, we may be disconnected */ |
| if (r == 0) { |
| bitrate = connectionStats.unifiTxDataRate; |
| } |
| } |
| |
| args->value = bitrate * 500000; |
| args->fixed = !flag; |
| |
| func_exit(); |
| |
| return 0; |
| } /* unifi_giwrate() */ |
| |
| |
| static int |
| unifi_siwrts(struct net_device *dev, struct iw_request_info *info, |
| union iwreq_data *wrqu, char *extra) |
| { |
| netInterface_priv_t *interfacePriv = (netInterface_priv_t *)netdev_priv(dev); |
| unifi_priv_t *priv = interfacePriv->privPtr; |
| int val = wrqu->rts.value; |
| int r = 0; |
| CsrWifiSmeMibConfig mibConfig; |
| |
| unifi_trace(priv, UDBG2, "unifi_siwrts\n"); |
| CHECK_INITED(priv); |
| |
| if (wrqu->rts.disabled) { |
| val = 2347; |
| } |
| |
| if ( (val < 0) || (val > 2347) ) |
| { |
| return -EINVAL; |
| } |
| |
| /* Get, modify and set the MIB data */ |
| UF_RTNL_UNLOCK(); |
| r = sme_mgt_mib_config_get(priv, &mibConfig); |
| UF_RTNL_LOCK(); |
| if (r) { |
| unifi_error(priv, "unifi_siwrts: Get CsrWifiSmeMibConfigValue failed.\n"); |
| return r; |
| } |
| mibConfig.dot11RtsThreshold = val; |
| UF_RTNL_UNLOCK(); |
| r = sme_mgt_mib_config_set(priv, &mibConfig); |
| UF_RTNL_LOCK(); |
| if (r) { |
| unifi_error(priv, "unifi_siwrts: Set CsrWifiSmeMibConfigValue failed.\n"); |
| return r; |
| } |
| |
| return 0; |
| } |
| |
| |
| static int |
| unifi_giwrts(struct net_device *dev, struct iw_request_info *info, |
| union iwreq_data *wrqu, char *extra) |
| { |
| netInterface_priv_t *interfacePriv = (netInterface_priv_t *)netdev_priv(dev); |
| unifi_priv_t *priv = interfacePriv->privPtr; |
| int r; |
| int rts_thresh; |
| CsrWifiSmeMibConfig mibConfig; |
| |
| unifi_trace(priv, UDBG2, "unifi_giwrts\n"); |
| CHECK_INITED(priv); |
| |
| UF_RTNL_UNLOCK(); |
| r = sme_mgt_mib_config_get(priv, &mibConfig); |
| UF_RTNL_LOCK(); |
| if (r) { |
| unifi_error(priv, "unifi_giwrts: Get CsrWifiSmeMibConfigValue failed.\n"); |
| return r; |
| } |
| |
| rts_thresh = mibConfig.dot11RtsThreshold; |
| if (rts_thresh > 2347) { |
| rts_thresh = 2347; |
| } |
| |
| wrqu->rts.value = rts_thresh; |
| wrqu->rts.disabled = (rts_thresh == 2347); |
| wrqu->rts.fixed = 1; |
| |
| return 0; |
| } |
| |
| |
| static int |
| unifi_siwfrag(struct net_device *dev, struct iw_request_info *info, |
| union iwreq_data *wrqu, char *extra) |
| { |
| netInterface_priv_t *interfacePriv = (netInterface_priv_t *)netdev_priv(dev); |
| unifi_priv_t *priv = interfacePriv->privPtr; |
| int val = wrqu->frag.value; |
| int r = 0; |
| CsrWifiSmeMibConfig mibConfig; |
| |
| unifi_trace(priv, UDBG2, "unifi_siwfrag\n"); |
| CHECK_INITED(priv); |
| |
| if (wrqu->frag.disabled) |
| val = 2346; |
| |
| if ( (val < 256) || (val > 2347) ) |
| return -EINVAL; |
| |
| /* Get, modify and set the MIB data */ |
| UF_RTNL_UNLOCK(); |
| r = sme_mgt_mib_config_get(priv, &mibConfig); |
| UF_RTNL_LOCK(); |
| if (r) { |
| unifi_error(priv, "unifi_siwfrag: Get CsrWifiSmeMibConfigValue failed.\n"); |
| return r; |
| } |
| /* Fragmentation Threashold must be even */ |
| mibConfig.dot11FragmentationThreshold = (val & ~0x1); |
| UF_RTNL_UNLOCK(); |
| r = sme_mgt_mib_config_set(priv, &mibConfig); |
| UF_RTNL_LOCK(); |
| if (r) { |
| unifi_error(priv, "unifi_siwfrag: Set CsrWifiSmeMibConfigValue failed.\n"); |
| return r; |
| } |
| |
| return 0; |
| } |
| |
| |
| static int |
| unifi_giwfrag(struct net_device *dev, struct iw_request_info *info, |
| union iwreq_data *wrqu, char *extra) |
| { |
| netInterface_priv_t *interfacePriv = (netInterface_priv_t *)netdev_priv(dev); |
| unifi_priv_t *priv = interfacePriv->privPtr; |
| int r; |
| int frag_thresh; |
| CsrWifiSmeMibConfig mibConfig; |
| |
| unifi_trace(priv, UDBG2, "unifi_giwfrag\n"); |
| CHECK_INITED(priv); |
| |
| UF_RTNL_UNLOCK(); |
| r = sme_mgt_mib_config_get(priv, &mibConfig); |
| UF_RTNL_LOCK(); |
| if (r) { |
| unifi_error(priv, "unifi_giwfrag: Get CsrWifiSmeMibConfigValue failed.\n"); |
| return r; |
| } |
| |
| frag_thresh = mibConfig.dot11FragmentationThreshold; |
| |
| /* Build the return structure */ |
| wrqu->frag.value = frag_thresh; |
| wrqu->frag.disabled = (frag_thresh >= 2346); |
| wrqu->frag.fixed = 1; |
| |
| return 0; |
| } |
| |
| |
| static int |
| unifi_siwencode(struct net_device *dev, struct iw_request_info *info, |
| union iwreq_data *wrqu, char *extra) |
| { |
| netInterface_priv_t *interfacePriv = (netInterface_priv_t *)netdev_priv(dev); |
| unifi_priv_t *priv = interfacePriv->privPtr; |
| struct iw_point *erq = &wrqu->encoding; |
| int index; |
| int rc = 0; |
| int privacy = -1; |
| CsrWifiSmeKey sme_key; |
| |
| func_enter(); |
| unifi_trace(priv, UDBG2, "unifi_siwencode\n"); |
| |
| CHECK_INITED(priv); |
| |
| if(interfacePriv->interfaceMode == CSR_WIFI_ROUTER_CTRL_MODE_AP || |
| interfacePriv->interfaceMode == CSR_WIFI_ROUTER_CTRL_MODE_P2PGO) { |
| unifi_error(priv, "unifi_siwencode: not permitted in Mode %d\n", |
| interfacePriv->interfaceMode); |
| return -EPERM; |
| } |
| |
| |
| /* |
| * Key index is encoded in the flags. |
| * 0 - use current default, |
| * 1-4 - if a key value is given set that key |
| * if not use that key |
| */ |
| index = (erq->flags & IW_ENCODE_INDEX); /* key number, 1-4 */ |
| if ((index < 0) || (index > 4)) { |
| unifi_error(priv, "unifi_siwencode: Request to set an invalid key (index:%d)", index); |
| return -EINVAL; |
| } |
| |
| /* |
| * Basic checking: do we have a key to set ? |
| * The IW_ENCODE_NOKEY flag is set when no key is present (only change flags), |
| * but older versions rely on sending a key id 1-4. |
| */ |
| if (erq->length > 0) { |
| |
| /* Check the size of the key */ |
| if ((erq->length > LARGE_KEY_SIZE) || (erq->length < SMALL_KEY_SIZE)) { |
| unifi_error(priv, "unifi_siwencode: Request to set an invalid key (length:%d)", |
| erq->length); |
| return -EINVAL; |
| } |
| |
| /* Check the index (none (i.e. 0) means use current) */ |
| if ((index < 1) || (index > 4)) { |
| /* If we do not have a previous key, use 1 as default */ |
| if (!priv->wep_tx_key_index) { |
| priv->wep_tx_key_index = 1; |
| } |
| index = priv->wep_tx_key_index; |
| } |
| |
| /* If we didn't have a key and a valid index is set, we want to remember it*/ |
| if (!priv->wep_tx_key_index) { |
| priv->wep_tx_key_index = index; |
| } |
| |
| unifi_trace(priv, UDBG1, "Tx key Index is %d\n", priv->wep_tx_key_index); |
| |
| privacy = 1; |
| |
| /* Check if the key is not marked as invalid */ |
| if ((erq->flags & IW_ENCODE_NOKEY) == 0) { |
| |
| unifi_trace(priv, UDBG1, "New %s key (len=%d, index=%d)\n", |
| (priv->wep_tx_key_index == index) ? "tx" : "", |
| erq->length, index); |
| |
| sme_key.wepTxKey = (priv->wep_tx_key_index == index); |
| if (priv->wep_tx_key_index == index) { |
| sme_key.keyType = CSR_WIFI_SME_KEY_TYPE_PAIRWISE; |
| } else { |
| sme_key.keyType = CSR_WIFI_SME_KEY_TYPE_GROUP; |
| } |
| /* Key index is zero based in SME but 1 based in wext */ |
| sme_key.keyIndex = (index - 1); |
| sme_key.keyLength = erq->length; |
| sme_key.authenticator = 0; |
| memset(sme_key.address.a, 0xFF, ETH_ALEN); |
| memcpy(sme_key.key, extra, erq->length); |
| |
| UF_RTNL_UNLOCK(); |
| rc = sme_mgt_key(priv, &sme_key, CSR_WIFI_SME_LIST_ACTION_ADD); |
| UF_RTNL_LOCK(); |
| if (rc) { |
| unifi_error(priv, "unifi_siwencode: Set key failed (%d)", rc); |
| return convert_sme_error(rc); |
| } |
| |
| /* Store the key to be reported by the SIOCGIWENCODE handler */ |
| priv->wep_keys[index - 1].len = erq->length; |
| memcpy(priv->wep_keys[index - 1].key, extra, erq->length); |
| } |
| } else { |
| /* |
| * No additional key data, so it must be a request to change the |
| * active key. |
| */ |
| if (index != 0) { |
| unifi_trace(priv, UDBG1, "Tx key Index is %d\n", index - 1); |
| |
| /* Store the index to be reported by the SIOCGIWENCODE handler */ |
| priv->wep_tx_key_index = index; |
| |
| sme_key.wepTxKey = 1; |
| sme_key.keyType = CSR_WIFI_SME_KEY_TYPE_PAIRWISE; |
| |
| /* Key index is zero based in SME but 1 based in wext */ |
| sme_key.keyIndex = (index - 1); |
| sme_key.keyLength = 0; |
| sme_key.authenticator = 0; |
| UF_RTNL_UNLOCK(); |
| rc = sme_mgt_key(priv, &sme_key, CSR_WIFI_SME_LIST_ACTION_ADD); |
| UF_RTNL_LOCK(); |
| if (rc) { |
| unifi_error(priv, "unifi_siwencode: Set key failed (%d)", rc); |
| return convert_sme_error(rc); |
| } |
| |
| /* Turn on encryption */ |
| privacy = 1; |
| } |
| } |
| |
| /* Read the flags */ |
| if (erq->flags & IW_ENCODE_DISABLED) { |
| /* disable encryption */ |
| unifi_trace(priv, UDBG1, "disable WEP encryption\n"); |
| privacy = 0; |
| |
| priv->wep_tx_key_index = 0; |
| |
| unifi_trace(priv, UDBG1, "IW_ENCODE_DISABLED: CSR_WIFI_SME_AUTH_MODE_80211_OPEN\n"); |
| priv->connection_config.authModeMask = CSR_WIFI_SME_AUTH_MODE_80211_OPEN; |
| } |
| |
| if (erq->flags & IW_ENCODE_RESTRICTED) { |
| /* Use shared key auth */ |
| unifi_trace(priv, UDBG1, "IW_ENCODE_RESTRICTED: CSR_WIFI_SME_AUTH_MODE_80211_SHARED\n"); |
| priv->connection_config.authModeMask = CSR_WIFI_SME_AUTH_MODE_80211_SHARED; |
| |
| /* Turn on encryption */ |
| privacy = 1; |
| } |
| if (erq->flags & IW_ENCODE_OPEN) { |
| unifi_trace(priv, UDBG1, "IW_ENCODE_OPEN: CSR_WIFI_SME_AUTH_MODE_80211_OPEN\n"); |
| priv->connection_config.authModeMask = CSR_WIFI_SME_AUTH_MODE_80211_OPEN; |
| } |
| |
| /* Commit the changes to flags if needed */ |
| if (privacy != -1) { |
| priv->connection_config.privacyMode = privacy ? CSR_WIFI_SME_80211_PRIVACY_MODE_ENABLED : CSR_WIFI_SME_80211_PRIVACY_MODE_DISABLED; |
| priv->connection_config.encryptionModeMask = privacy ? (CSR_WIFI_SME_ENCRYPTION_CIPHER_PAIRWISE_WEP40 | |
| CSR_WIFI_SME_ENCRYPTION_CIPHER_PAIRWISE_WEP104 | |
| CSR_WIFI_SME_ENCRYPTION_CIPHER_GROUP_WEP40 | |
| CSR_WIFI_SME_ENCRYPTION_CIPHER_GROUP_WEP104) : |
| CSR_WIFI_SME_ENCRYPTION_CIPHER_NONE; |
| } |
| |
| func_exit_r(rc); |
| return convert_sme_error(rc); |
| |
| } /* unifi_siwencode() */ |
| |
| |
| |
| static int |
| unifi_giwencode(struct net_device *dev, struct iw_request_info *info, |
| union iwreq_data *wrqu, char *extra) |
| { |
| netInterface_priv_t *interfacePriv = (netInterface_priv_t *)netdev_priv(dev); |
| unifi_priv_t *priv = interfacePriv->privPtr; |
| struct iw_point *erq = &wrqu->encoding; |
| |
| unifi_trace(priv, UDBG2, "unifi_giwencode\n"); |
| |
| CHECK_INITED(priv); |
| |
| if(interfacePriv->interfaceMode == CSR_WIFI_ROUTER_CTRL_MODE_AP || |
| interfacePriv->interfaceMode == CSR_WIFI_ROUTER_CTRL_MODE_P2PGO) { |
| unifi_error(priv, "unifi_giwencode: not permitted in Mode %d\n", |
| interfacePriv->interfaceMode); |
| return -EPERM; |
| } |
| |
| |
| if (priv->connection_config.authModeMask == CSR_WIFI_SME_AUTH_MODE_80211_SHARED) { |
| erq->flags = IW_ENCODE_RESTRICTED; |
| } |
| else { |
| if (priv->connection_config.privacyMode == CSR_WIFI_SME_80211_PRIVACY_MODE_DISABLED) { |
| erq->flags = IW_ENCODE_DISABLED; |
| } else { |
| erq->flags = IW_ENCODE_OPEN; |
| } |
| } |
| |
| erq->length = 0; |
| |
| if (erq->flags != IW_ENCODE_DISABLED) { |
| int index = priv->wep_tx_key_index; |
| |
| if ((index > 0) && (index <= NUM_WEPKEYS)) { |
| erq->flags |= (index & IW_ENCODE_INDEX); |
| erq->length = priv->wep_keys[index - 1].len; |
| memcpy(extra, priv->wep_keys[index - 1].key, erq->length); |
| } else { |
| unifi_notice(priv, "unifi_giwencode: Surprise, do not have a valid key index (%d)\n", |
| index); |
| } |
| } |
| |
| return 0; |
| } /* unifi_giwencode() */ |
| |
| |
| static int |
| unifi_siwpower(struct net_device *dev, struct iw_request_info *info, |
| union iwreq_data *wrqu, char *extra) |
| { |
| struct iw_param *args = &wrqu->power; |
| netInterface_priv_t *interfacePriv = (netInterface_priv_t *)netdev_priv(dev); |
| unifi_priv_t *priv = interfacePriv->privPtr; |
| int listen_interval, wake_for_dtim; |
| int r = 0; |
| CsrWifiSmePowerConfig powerConfig; |
| |
| unifi_trace(priv, UDBG2, "unifi_siwpower\n"); |
| |
| CHECK_INITED(priv); |
| |
| if(interfacePriv->interfaceMode == CSR_WIFI_ROUTER_CTRL_MODE_AP || |
| interfacePriv->interfaceMode == CSR_WIFI_ROUTER_CTRL_MODE_P2PGO) { |
| unifi_error(priv, "unifi_siwpower: not permitted in Mode %d\n", |
| interfacePriv->interfaceMode); |
| return -EPERM; |
| } |
| |
| UF_RTNL_UNLOCK(); |
| r = sme_mgt_power_config_get(priv, &powerConfig); |
| UF_RTNL_LOCK(); |
| if (r) { |
| unifi_error(priv, "unifi_siwpower: Get unifi_PowerConfigValue failed.\n"); |
| return r; |
| } |
| |
| listen_interval = -1; |
| wake_for_dtim = -1; |
| if (args->disabled) { |
| powerConfig.powerSaveLevel = CSR_WIFI_SME_POWER_SAVE_LEVEL_LOW; |
| } |
| else |
| { |
| powerConfig.powerSaveLevel = CSR_WIFI_SME_POWER_SAVE_LEVEL_HIGH; |
| |
| switch (args->flags & IW_POWER_TYPE) { |
| case 0: |
| /* not specified */ |
| break; |
| case IW_POWER_PERIOD: |
| listen_interval = args->value / 1000; |
| break; |
| default: |
| return -EINVAL; |
| } |
| |
| switch (args->flags & IW_POWER_MODE) { |
| case 0: |
| /* not specified */ |
| break; |
| case IW_POWER_UNICAST_R: |
| /* not interested in broadcast packets */ |
| wake_for_dtim = 0; |
| break; |
| case IW_POWER_ALL_R: |
| /* yes, we are interested in broadcast packets */ |
| wake_for_dtim = 1; |
| break; |
| default: |
| return -EINVAL; |
| } |
| } |
| |
| if (listen_interval > 0) { |
| powerConfig.listenIntervalTu = listen_interval; |
| unifi_trace(priv, UDBG4, "unifi_siwpower: new Listen Interval = %d.\n", |
| powerConfig.listenIntervalTu); |
| } |
| |
| if (wake_for_dtim >= 0) { |
| powerConfig.rxDtims = wake_for_dtim; |
| } |
| UF_RTNL_UNLOCK(); |
| r = sme_mgt_power_config_set(priv, &powerConfig); |
| UF_RTNL_LOCK(); |
| if (r) { |
| unifi_error(priv, "unifi_siwpower: Set unifi_PowerConfigValue failed.\n"); |
| return r; |
| } |
| |
| return 0; |
| } /* unifi_siwpower() */ |
| |
| |
| static int |
| unifi_giwpower(struct net_device *dev, struct iw_request_info *info, |
| union iwreq_data *wrqu, char *extra) |
| { |
| struct iw_param *args = &wrqu->power; |
| netInterface_priv_t *interfacePriv = (netInterface_priv_t *)netdev_priv(dev); |
| unifi_priv_t *priv = interfacePriv->privPtr; |
| CsrWifiSmePowerConfig powerConfig; |
| int r; |
| |
| unifi_trace(priv, UDBG2, "unifi_giwpower\n"); |
| |
| CHECK_INITED(priv); |
| |
| if(interfacePriv->interfaceMode == CSR_WIFI_ROUTER_CTRL_MODE_AP || |
| interfacePriv->interfaceMode == CSR_WIFI_ROUTER_CTRL_MODE_P2PGO) { |
| unifi_error(priv, "unifi_giwpower: not permitted in Mode %d\n", |
| interfacePriv->interfaceMode); |
| return -EPERM; |
| } |
| |
| |
| args->flags = 0; |
| UF_RTNL_UNLOCK(); |
| r = sme_mgt_power_config_get(priv, &powerConfig); |
| UF_RTNL_LOCK(); |
| if (r) { |
| unifi_error(priv, "unifi_giwpower: Get unifi_PowerConfigValue failed.\n"); |
| return r; |
| } |
| |
| unifi_trace(priv, UDBG4, "unifi_giwpower: mode=%d\n", |
| powerConfig.powerSaveLevel); |
| |
| args->disabled = (powerConfig.powerSaveLevel == CSR_WIFI_SME_POWER_SAVE_LEVEL_LOW); |
| if (args->disabled) { |
| args->flags = 0; |
| return 0; |
| } |
| |
| args->value = powerConfig.listenIntervalTu * 1000; |
| args->flags |= IW_POWER_PERIOD; |
| |
| if (powerConfig.rxDtims) { |
| args->flags |= IW_POWER_ALL_R; |
| } else { |
| args->flags |= IW_POWER_UNICAST_R; |
| } |
| |
| return 0; |
| } /* unifi_giwpower() */ |
| |
| |
| /* |
| * --------------------------------------------------------------------------- |
| * unifi_siwcommit - handler for SIOCSIWCOMMIT |
| * |
| * Apply all the parameters that have been set. |
| * In practice this means: |
| * - do a scan |
| * - join a network or start an AdHoc |
| * - authenticate and associate. |
| * |
| * Arguments: |
| * None. |
| * |
| * Returns: |
| * None. |
| * --------------------------------------------------------------------------- |
| */ |
| static int |
| unifi_siwcommit(struct net_device *dev, struct iw_request_info *info, |
| union iwreq_data *wrqu, char *extra) |
| { |
| return 0; |
| } /* unifi_siwcommit() */ |
| |
| |
| |
| static int |
| unifi_siwmlme(struct net_device *dev, struct iw_request_info *info, |
| union iwreq_data *wrqu, char *extra) |
| { |
| netInterface_priv_t *interfacePriv = (netInterface_priv_t *)netdev_priv(dev); |
| unifi_priv_t *priv = interfacePriv->privPtr; |
| struct iw_mlme *mlme = (struct iw_mlme *)extra; |
| func_enter(); |
| |
| unifi_trace(priv, UDBG2, "unifi_siwmlme\n"); |
| CHECK_INITED(priv); |
| |
| if(interfacePriv->interfaceMode == CSR_WIFI_ROUTER_CTRL_MODE_AP || |
| interfacePriv->interfaceMode == CSR_WIFI_ROUTER_CTRL_MODE_P2PGO) { |
| unifi_error(priv, "unifi_siwmlme: not permitted in Mode %d\n", |
| interfacePriv->interfaceMode); |
| return -EPERM; |
| } |
| |
| |
| switch (mlme->cmd) { |
| case IW_MLME_DEAUTH: |
| case IW_MLME_DISASSOC: |
| UF_RTNL_UNLOCK(); |
| sme_mgt_disconnect(priv); |
| UF_RTNL_LOCK(); |
| break; |
| default: |
| func_exit_r(-EOPNOTSUPP); |
| return -EOPNOTSUPP; |
| } |
| |
| func_exit(); |
| return 0; |
| } /* unifi_siwmlme() */ |
| |
| |
| /* |
| * --------------------------------------------------------------------------- |
| * unifi_siwgenie |
| * unifi_giwgenie |
| * |
| * WPA : Generic IEEE 802.11 information element (e.g., for WPA/RSN/WMM). |
| * Handlers for SIOCSIWGENIE, SIOCGIWGENIE - set/get generic IE |
| * |
| * The host program (e.g. wpa_supplicant) uses this call to set the |
| * additional IEs to accompany the next (Associate?) request. |
| * |
| * Arguments: |
| * None. |
| * |
| * Returns: |
| * None. |
| * Notes: |
| * From wireless.h: |
| * This ioctl uses struct iw_point and data buffer that includes IE id |
| * and len fields. More than one IE may be included in the |
| * request. Setting the generic IE to empty buffer (len=0) removes the |
| * generic IE from the driver. |
| * --------------------------------------------------------------------------- |
| */ |
| static int |
| unifi_siwgenie(struct net_device *dev, struct iw_request_info *info, |
| union iwreq_data *wrqu, char *extra) |
| { |
| netInterface_priv_t *interfacePriv = (netInterface_priv_t *)netdev_priv(dev); |
| unifi_priv_t *priv = interfacePriv->privPtr; |
| int len; |
| |
| func_enter(); |
| unifi_trace(priv, UDBG2, "unifi_siwgenie\n"); |
| |
| if(interfacePriv->interfaceMode == CSR_WIFI_ROUTER_CTRL_MODE_AP || |
| interfacePriv->interfaceMode == CSR_WIFI_ROUTER_CTRL_MODE_P2PGO) { |
| unifi_error(priv, "unifi_siwgenie: not permitted in Mode %d\n", |
| interfacePriv->interfaceMode); |
| return -EPERM; |
| } |
| |
| |
| if ( priv->connection_config.mlmeAssociateReqInformationElements) { |
| kfree( priv->connection_config.mlmeAssociateReqInformationElements); |
| } |
| priv->connection_config.mlmeAssociateReqInformationElementsLength = 0; |
| priv->connection_config.mlmeAssociateReqInformationElements = NULL; |
| |
| len = wrqu->data.length; |
| if (len == 0) { |
| func_exit(); |
| return 0; |
| } |
| |
| priv->connection_config.mlmeAssociateReqInformationElements = kmalloc(len, GFP_KERNEL); |
| if (priv->connection_config.mlmeAssociateReqInformationElements == NULL) { |
| func_exit(); |
| return -ENOMEM; |
| } |
| |
| priv->connection_config.mlmeAssociateReqInformationElementsLength = len; |
| memcpy( priv->connection_config.mlmeAssociateReqInformationElements, extra, len); |
| |
| func_exit(); |
| return 0; |
| } /* unifi_siwgenie() */ |
| |
| |
| static int |
| unifi_giwgenie(struct net_device *dev, struct iw_request_info *info, |
| union iwreq_data *wrqu, char *extra) |
| { |
| netInterface_priv_t *interfacePriv = (netInterface_priv_t *)netdev_priv(dev); |
| unifi_priv_t *priv = interfacePriv->privPtr; |
| int len; |
| |
| func_enter(); |
| unifi_trace(priv, UDBG2, "unifi_giwgenie\n"); |
| |
| if(interfacePriv->interfaceMode == CSR_WIFI_ROUTER_CTRL_MODE_AP || |
| interfacePriv->interfaceMode == CSR_WIFI_ROUTER_CTRL_MODE_P2PGO) { |
| unifi_error(priv, "unifi_giwgenie: not permitted in Mode %d\n", |
| interfacePriv->interfaceMode); |
| return -EPERM; |
| } |
| |
| |
| len = priv->connection_config.mlmeAssociateReqInformationElementsLength; |
| |
| if (len == 0) { |
| wrqu->data.length = 0; |
| return 0; |
| } |
| |
| if (wrqu->data.length < len) { |
| return -E2BIG; |
| } |
| |
| wrqu->data.length = len; |
| memcpy(extra, priv->connection_config.mlmeAssociateReqInformationElements, len); |
| |
| func_exit(); |
| return 0; |
| } /* unifi_giwgenie() */ |
| |
| |
| /* |
| * --------------------------------------------------------------------------- |
| * unifi_siwauth |
| * unifi_giwauth |
| * |
| * Handlers for SIOCSIWAUTH, SIOCGIWAUTH |
| * Set/get various authentication parameters. |
| * |
| * Arguments: |
| * |
| * |
| * Returns: |
| * None. |
| * --------------------------------------------------------------------------- |
| */ |
| static int |
| _unifi_siwauth(struct net_device *dev, struct iw_request_info *info, |
| union iwreq_data *wrqu, char *extra) |
| { |
| netInterface_priv_t *interfacePriv = (netInterface_priv_t *)netdev_priv(dev); |
| unifi_priv_t *priv = interfacePriv->privPtr; |
| CsrWifiSmeAuthModeMask new_auth; |
| |
| func_enter(); |
| unifi_trace(priv, UDBG2, "unifi_siwauth\n"); |
| |
| if(interfacePriv->interfaceMode == CSR_WIFI_ROUTER_CTRL_MODE_AP || |
| interfacePriv->interfaceMode == CSR_WIFI_ROUTER_CTRL_MODE_P2PGO) { |
| unifi_error(priv, "unifi_siwauth: not permitted in Mode %d\n", |
| interfacePriv->interfaceMode); |
| return -EPERM; |
| } |
| |
| |
| /* |
| * This ioctl is safe to call even when UniFi is powered off. |
| * wpa_supplicant calls it to test whether we support WPA. |
| */ |
| |
| switch (wrqu->param.flags & IW_AUTH_INDEX) { |
| |
| case IW_AUTH_WPA_ENABLED: |
| unifi_trace(priv, UDBG1, "IW_AUTH_WPA_ENABLED: %d\n", wrqu->param.value); |
| |
| if (wrqu->param.value == 0) { |
| unifi_trace(priv, UDBG5, "IW_AUTH_WPA_ENABLED: CSR_WIFI_SME_AUTH_MODE_80211_OPEN\n"); |
| priv->connection_config.authModeMask = CSR_WIFI_SME_AUTH_MODE_80211_OPEN; |
| } |
| break; |
| |
| case IW_AUTH_PRIVACY_INVOKED: |
| unifi_trace(priv, UDBG1, "IW_AUTH_PRIVACY_INVOKED: %d\n", wrqu->param.value); |
| |
| priv->connection_config.privacyMode = wrqu->param.value ? CSR_WIFI_SME_80211_PRIVACY_MODE_ENABLED : CSR_WIFI_SME_80211_PRIVACY_MODE_DISABLED; |
| if (wrqu->param.value == CSR_WIFI_SME_80211_PRIVACY_MODE_DISABLED) |
| { |
| priv->connection_config.encryptionModeMask = CSR_WIFI_SME_ENCRYPTION_CIPHER_NONE; |
| } |
| break; |
| |
| case IW_AUTH_80211_AUTH_ALG: |
| /* |
| IW_AUTH_ALG_OPEN_SYSTEM 0x00000001 |
| IW_AUTH_ALG_SHARED_KEY 0x00000002 |
| IW_AUTH_ALG_LEAP 0x00000004 |
| */ |
| new_auth = 0; |
| if (wrqu->param.value & IW_AUTH_ALG_OPEN_SYSTEM) { |
| unifi_trace(priv, UDBG1, "IW_AUTH_80211_AUTH_ALG: %d (IW_AUTH_ALG_OPEN_SYSTEM)\n", wrqu->param.value); |
| new_auth |= CSR_WIFI_SME_AUTH_MODE_80211_OPEN; |
| } |
| if (wrqu->param.value & IW_AUTH_ALG_SHARED_KEY) { |
| unifi_trace(priv, UDBG1, "IW_AUTH_80211_AUTH_ALG: %d (IW_AUTH_ALG_SHARED_KEY)\n", wrqu->param.value); |
| new_auth |= CSR_WIFI_SME_AUTH_MODE_80211_SHARED; |
| } |
| if (wrqu->param.value & IW_AUTH_ALG_LEAP) { |
| /* Initial exchanges using open-system to set EAP */ |
| unifi_trace(priv, UDBG1, "IW_AUTH_80211_AUTH_ALG: %d (IW_AUTH_ALG_LEAP)\n", wrqu->param.value); |
| new_auth |= CSR_WIFI_SME_AUTH_MODE_8021X_OTHER1X; |
| } |
| if (new_auth == 0) { |
| unifi_trace(priv, UDBG1, "IW_AUTH_80211_AUTH_ALG: invalid value %d\n", |
| wrqu->param.value); |
| return -EINVAL; |
| } else { |
| priv->connection_config.authModeMask = new_auth; |
| } |
| break; |
| |
| case IW_AUTH_WPA_VERSION: |
| unifi_trace(priv, UDBG1, "IW_AUTH_WPA_VERSION: %d\n", wrqu->param.value); |
| priv->ignore_bssid_join = TRUE; |
| /* |
| IW_AUTH_WPA_VERSION_DISABLED 0x00000001 |
| IW_AUTH_WPA_VERSION_WPA 0x00000002 |
| IW_AUTH_WPA_VERSION_WPA2 0x00000004 |
| */ |
| |
| if (!(wrqu->param.value & IW_AUTH_WPA_VERSION_DISABLED)) { |
| |
| priv->connection_config.authModeMask = CSR_WIFI_SME_AUTH_MODE_80211_OPEN; |
| |
| if (wrqu->param.value & IW_AUTH_WPA_VERSION_WPA) { |
| unifi_trace(priv, UDBG4, "IW_AUTH_WPA_VERSION: WPA, WPA-PSK\n"); |
| priv->connection_config.authModeMask |= (CSR_WIFI_SME_AUTH_MODE_8021X_WPA | CSR_WIFI_SME_AUTH_MODE_8021X_WPAPSK); |
| } |
| if (wrqu->param.value & IW_AUTH_WPA_VERSION_WPA2) { |
| unifi_trace(priv, UDBG4, "IW_AUTH_WPA_VERSION: WPA2, WPA2-PSK\n"); |
| priv->connection_config.authModeMask |= (CSR_WIFI_SME_AUTH_MODE_8021X_WPA2 | CSR_WIFI_SME_AUTH_MODE_8021X_WPA2PSK); |
| } |
| } |
| break; |
| |
| case IW_AUTH_CIPHER_PAIRWISE: |
| unifi_trace(priv, UDBG1, "IW_AUTH_CIPHER_PAIRWISE: %d\n", wrqu->param.value); |
| /* |
| * one of: |
| IW_AUTH_CIPHER_NONE 0x00000001 |
| IW_AUTH_CIPHER_WEP40 0x00000002 |
| IW_AUTH_CIPHER_TKIP 0x00000004 |
| IW_AUTH_CIPHER_CCMP 0x00000008 |
| IW_AUTH_CIPHER_WEP104 0x00000010 |
| */ |
| |
| priv->connection_config.encryptionModeMask = CSR_WIFI_SME_ENCRYPTION_CIPHER_NONE; |
| |
| if (wrqu->param.value & IW_AUTH_CIPHER_WEP40) { |
| priv->connection_config.encryptionModeMask |= |
| CSR_WIFI_SME_ENCRYPTION_CIPHER_PAIRWISE_WEP40 | CSR_WIFI_SME_ENCRYPTION_CIPHER_GROUP_WEP40; |
| } |
| if (wrqu->param.value & IW_AUTH_CIPHER_WEP104) { |
| priv->connection_config.encryptionModeMask |= |
| CSR_WIFI_SME_ENCRYPTION_CIPHER_PAIRWISE_WEP104 | CSR_WIFI_SME_ENCRYPTION_CIPHER_GROUP_WEP104; |
| } |
| if (wrqu->param.value & IW_AUTH_CIPHER_TKIP) { |
| priv->connection_config.encryptionModeMask |= |
| CSR_WIFI_SME_ENCRYPTION_CIPHER_PAIRWISE_TKIP | CSR_WIFI_SME_ENCRYPTION_CIPHER_GROUP_TKIP; |
| } |
| if (wrqu->param.value & IW_AUTH_CIPHER_CCMP) { |
| priv->connection_config.encryptionModeMask |= |
| CSR_WIFI_SME_ENCRYPTION_CIPHER_PAIRWISE_CCMP | CSR_WIFI_SME_ENCRYPTION_CIPHER_GROUP_CCMP; |
| } |
| |
| break; |
| |
| case IW_AUTH_CIPHER_GROUP: |
| unifi_trace(priv, UDBG1, "IW_AUTH_CIPHER_GROUP: %d\n", wrqu->param.value); |
| /* |
| * Use the WPA version and the group cipher suite to set the permitted |
| * group key in the MIB. f/w uses this value to validate WPA and RSN IEs |
| * in the probe responses from the desired BSS(ID) |
| */ |
| |
| priv->connection_config.encryptionModeMask &= ~(CSR_WIFI_SME_ENCRYPTION_CIPHER_GROUP_WEP40 | |
| CSR_WIFI_SME_ENCRYPTION_CIPHER_GROUP_WEP104 | |
| CSR_WIFI_SME_ENCRYPTION_CIPHER_GROUP_TKIP | |
| CSR_WIFI_SME_ENCRYPTION_CIPHER_GROUP_CCMP); |
| if (wrqu->param.value & IW_AUTH_CIPHER_WEP40) { |
| priv->connection_config.encryptionModeMask |= CSR_WIFI_SME_ENCRYPTION_CIPHER_GROUP_WEP40; |
| } |
| if (wrqu->param.value & IW_AUTH_CIPHER_WEP104) { |
| priv->connection_config.encryptionModeMask |= CSR_WIFI_SME_ENCRYPTION_CIPHER_GROUP_WEP104; |
| } |
| if (wrqu->param.value & IW_AUTH_CIPHER_TKIP) { |
| priv->connection_config.encryptionModeMask |= CSR_WIFI_SME_ENCRYPTION_CIPHER_GROUP_TKIP; |
| } |
| if (wrqu->param.value & IW_AUTH_CIPHER_CCMP) { |
| priv->connection_config.encryptionModeMask |= CSR_WIFI_SME_ENCRYPTION_CIPHER_GROUP_CCMP; |
| } |
| |
| break; |
| |
| case IW_AUTH_KEY_MGMT: |
| unifi_trace(priv, UDBG1, "IW_AUTH_KEY_MGMT: %d\n", wrqu->param.value); |
| /* |
| IW_AUTH_KEY_MGMT_802_1X 1 |
| IW_AUTH_KEY_MGMT_PSK 2 |
| */ |
| if (priv->connection_config.authModeMask & (CSR_WIFI_SME_AUTH_MODE_8021X_WPA | CSR_WIFI_SME_AUTH_MODE_8021X_WPAPSK)) { |
| /* Check for explicitly set mode. */ |
| if (wrqu->param.value == IW_AUTH_KEY_MGMT_802_1X) { |
| priv->connection_config.authModeMask &= ~CSR_WIFI_SME_AUTH_MODE_8021X_WPAPSK; |
| } |
| if (wrqu->param.value == IW_AUTH_KEY_MGMT_PSK) { |
| priv->connection_config.authModeMask &= ~CSR_WIFI_SME_AUTH_MODE_8021X_WPA; |
| } |
| unifi_trace(priv, UDBG5, "IW_AUTH_KEY_MGMT: WPA: %d\n", |
| priv->connection_config.authModeMask); |
| } |
| if (priv->connection_config.authModeMask & (CSR_WIFI_SME_AUTH_MODE_8021X_WPA2 | CSR_WIFI_SME_AUTH_MODE_8021X_WPA2PSK)) { |
| /* Check for explicitly set mode. */ |
| if (wrqu->param.value == IW_AUTH_KEY_MGMT_802_1X) { |
| priv->connection_config.authModeMask &= ~CSR_WIFI_SME_AUTH_MODE_8021X_WPA2PSK; |
| } |
| if (wrqu->param.value == IW_AUTH_KEY_MGMT_PSK) { |
| priv->connection_config.authModeMask &= ~CSR_WIFI_SME_AUTH_MODE_8021X_WPA2; |
| } |
| unifi_trace(priv, UDBG5, "IW_AUTH_KEY_MGMT: WPA2: %d\n", |
| priv->connection_config.authModeMask); |
| } |
| |
| break; |
| case IW_AUTH_TKIP_COUNTERMEASURES: |
| /* |
| * Set to true at the start of the 60 second backup-off period |
| * following 2 MichaelMIC failures within 60s. |
| */ |
| unifi_trace(priv, UDBG1, "IW_AUTH_TKIP_COUNTERMEASURES: %d\n", wrqu->param.value); |
| break; |
| |
| case IW_AUTH_DROP_UNENCRYPTED: |
| /* |
| * Set to true on init. |
| * Set to false just before associate if encryption will not be |
| * required. |
| * |
| * Note this is not the same as the 802.1X controlled port |
| */ |
| unifi_trace(priv, UDBG1, "IW_AUTH_DROP_UNENCRYPTED: %d\n", wrqu->param.value); |
| break; |
| |
| case IW_AUTH_RX_UNENCRYPTED_EAPOL: |
| /* |
| * This is set by wpa_supplicant to allow unencrypted EAPOL messages |
| * even if pairwise keys are set when not using WPA. IEEE 802.1X |
| * specifies that these frames are not encrypted, but WPA encrypts |
| * them when pairwise keys are in use. |
| * I think the UniFi f/w handles this decision for us. |
| */ |
| unifi_trace(priv, UDBG1, "IW_AUTH_RX_UNENCRYPTED_EAPOL: %d\n", wrqu->param.value); |
| break; |
| |
| case IW_AUTH_ROAMING_CONTROL: |
| unifi_trace(priv, UDBG1, "IW_AUTH_ROAMING_CONTROL: %d\n", wrqu->param.value); |
| break; |
| |
| default: |
| unifi_trace(priv, UDBG1, "Unsupported auth param %d to 0x%X\n", |
| wrqu->param.flags & IW_AUTH_INDEX, |
| wrqu->param.value); |
| return -EOPNOTSUPP; |
| } |
| |
| unifi_trace(priv, UDBG2, "authModeMask = %d", priv->connection_config.authModeMask); |
| func_exit(); |
| |
| return 0; |
| } /* _unifi_siwauth() */ |
| |
| |
| static int |
| unifi_siwauth(struct net_device *dev, struct iw_request_info *info, |
| union iwreq_data *wrqu, char *extra) |
| { |
| int err = 0; |
| |
| UF_RTNL_UNLOCK(); |
| err = _unifi_siwauth(dev, info, wrqu, extra); |
| UF_RTNL_LOCK(); |
| |
| return err; |
| } /* unifi_siwauth() */ |
| |
| |
| static int |
| unifi_giwauth(struct net_device *dev, struct iw_request_info *info, |
| union iwreq_data *wrqu, char *extra) |
| { |
| unifi_trace(NULL, UDBG2, "unifi_giwauth\n"); |
| return -EOPNOTSUPP; |
| } /* unifi_giwauth() */ |
| |
| /* |
| * --------------------------------------------------------------------------- |
| * unifi_siwencodeext |
| * unifi_giwencodeext |
| * |
| * Handlers for SIOCSIWENCODEEXT, SIOCGIWENCODEEXT - set/get |
| * encoding token & mode |
| * |
| * Arguments: |
| * None. |
| * |
| * Returns: |
| * None. |
| * |
| * Notes: |
| * For WPA/WPA2 we don't take note of the IW_ENCODE_EXT_SET_TX_KEY flag. |
| * This flag means "use this key to encode transmissions"; we just |
| * assume only one key will be set and that is the one to use. |
| * --------------------------------------------------------------------------- |
| */ |
| static int |
| _unifi_siwencodeext(struct net_device *dev, struct iw_request_info *info, |
| union iwreq_data *wrqu, char *extra) |
| { |
| netInterface_priv_t *interfacePriv = (netInterface_priv_t *)netdev_priv(dev); |
| unifi_priv_t *priv = interfacePriv->privPtr; |
| struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; |
| int r = 0; |
| unsigned char *keydata; |
| unsigned char tkip_key[32]; |
| int keyid; |
| unsigned char *a = (unsigned char *)ext->addr.sa_data; |
| CsrWifiSmeKey sme_key; |
| CsrWifiSmeKeyType key_type; |
| |
| func_enter(); |
| |
| CHECK_INITED(priv); |
| |
| if(interfacePriv->interfaceMode == CSR_WIFI_ROUTER_CTRL_MODE_AP || |
| interfacePriv->interfaceMode == CSR_WIFI_ROUTER_CTRL_MODE_P2PGO) { |
| unifi_error(priv, "unifi_siwencodeext: not permitted in Mode %d\n", |
| interfacePriv->interfaceMode); |
| return -EPERM; |
| } |
| |
| |
| unifi_trace(priv, UDBG1, "siwencodeext: flags=0x%X, alg=%d, ext_flags=0x%X, len=%d, index=%d,\n", |
| wrqu->encoding.flags, ext->alg, ext->ext_flags, |
| ext->key_len, (wrqu->encoding.flags & IW_ENCODE_INDEX)); |
| unifi_trace(priv, UDBG3, " addr=%pM\n", a); |
| |
| if ((ext->key_len == 0) && (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY)) { |
| /* This means use a different key (given by key_idx) for Tx. */ |
| /* NYI */ |
| unifi_trace(priv, UDBG1, KERN_ERR "unifi_siwencodeext: NYI should change tx key id here!!\n"); |
| return -ENOTSUPP; |
| } |
| |
| memset(&sme_key, 0, sizeof(sme_key)); |
| |
| keydata = (unsigned char *)(ext + 1); |
| keyid = (wrqu->encoding.flags & IW_ENCODE_INDEX); |
| |
| /* |
| * Check for request to delete keys for an address. |
| */ |
| /* Pick out request for no privacy. */ |
| if (ext->alg == IW_ENCODE_ALG_NONE) { |
| |
| unifi_trace(priv, UDBG1, "Deleting %s key %d\n", |
| (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) ? "GROUP" : "PAIRWISE", |
| keyid); |
| |
| if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) { |
| sme_key.keyType = CSR_WIFI_SME_KEY_TYPE_GROUP; |
| } else { |
| sme_key.keyType = CSR_WIFI_SME_KEY_TYPE_PAIRWISE; |
| } |
| sme_key.keyIndex = (keyid - 1); |
| sme_key.keyLength = 0; |
| sme_key.authenticator = 0; |
| memcpy(sme_key.address.a, a, ETH_ALEN); |
| UF_RTNL_UNLOCK(); |
| r = sme_mgt_key(priv, &sme_key, CSR_WIFI_SME_LIST_ACTION_REMOVE); |
| UF_RTNL_LOCK(); |
| if (r) { |
| unifi_error(priv, "Delete key request was rejected with result %d\n", r); |
| return convert_sme_error(r); |
| } |
| |
| return 0; |
| } |
| |
| /* |
| * Request is to set a key, not delete |
| */ |
| |
| /* Pick out WEP and use set_wep_key(). */ |
| if (ext->alg == IW_ENCODE_ALG_WEP) { |
| /* WEP-40, WEP-104 */ |
| |
| /* Check for valid key length */ |
| if (!((ext->key_len == 5) || (ext->key_len == 13))) { |
| unifi_trace(priv, UDBG1, KERN_ERR "Invalid length for WEP key: %d\n", ext->key_len); |
| return -EINVAL; |
| } |
| |
| unifi_trace(priv, UDBG1, "Setting WEP key %d tx:%d\n", |
| keyid, ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY); |
| |
| if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) { |
| sme_key.wepTxKey = TRUE; |
| sme_key.keyType = CSR_WIFI_SME_KEY_TYPE_PAIRWISE; |
| } else { |
| sme_key.wepTxKey = FALSE; |
| sme_key.keyType = CSR_WIFI_SME_KEY_TYPE_GROUP; |
| } |
| sme_key.keyIndex = (keyid - 1); |
| sme_key.keyLength = ext->key_len; |
| sme_key.authenticator = 0; |
| memset(sme_key.address.a, 0xFF, ETH_ALEN); |
| memcpy(sme_key.key, keydata, ext->key_len); |
| UF_RTNL_UNLOCK(); |
| r = sme_mgt_key(priv, &sme_key, CSR_WIFI_SME_LIST_ACTION_ADD); |
| UF_RTNL_LOCK(); |
| if (r) { |
| unifi_error(priv, "siwencodeext: Set key failed (%d)", r); |
| return convert_sme_error(r); |
| } |
| |
| return 0; |
| } |
| |
| /* |
| * |
| * If we reach here, we are dealing with a WPA/WPA2 key |
| * |
| */ |
| if (ext->key_len > 32) { |
| return -EINVAL; |
| } |
| |
| /* |
| * TKIP keys from wpa_supplicant need swapping. |
| * What about other supplicants (when they come along)? |
| */ |
| if ((ext->alg == IW_ENCODE_ALG_TKIP) && (ext->key_len == 32)) { |
| memcpy(tkip_key, keydata, 16); |
| memcpy(tkip_key + 16, keydata + 24, 8); |
| memcpy(tkip_key + 24, keydata + 16, 8); |
| keydata = tkip_key; |
| } |
| |
| key_type = (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) ? |
| CSR_WIFI_SME_KEY_TYPE_GROUP : /* Group Key */ |
| CSR_WIFI_SME_KEY_TYPE_PAIRWISE; /* Pairwise Key */ |
| |
| sme_key.keyType = key_type; |
| sme_key.keyIndex = (keyid - 1); |
| sme_key.keyLength = ext->key_len; |
| sme_key.authenticator = 0; |
| memcpy(sme_key.address.a, ext->addr.sa_data, ETH_ALEN); |
| if (ext->ext_flags & IW_ENCODE_EXT_RX_SEQ_VALID) { |
| |
| unifi_trace(priv, UDBG5, "RSC first 6 bytes = %*phC\n", |
| 6, ext->rx_seq); |
| |
| /* memcpy((u8*)(&sme_key.keyRsc), ext->rx_seq, 8); */ |
| sme_key.keyRsc[0] = ext->rx_seq[1] << 8 | ext->rx_seq[0]; |
| sme_key.keyRsc[1] = ext->rx_seq[3] << 8 | ext->rx_seq[2]; |
| sme_key.keyRsc[2] = ext->rx_seq[5] << 8 | ext->rx_seq[4]; |
| sme_key.keyRsc[3] = ext->rx_seq[7] << 8 | ext->rx_seq[6]; |
| |
| } |
| |
| memcpy(sme_key.key, keydata, ext->key_len); |
| UF_RTNL_UNLOCK(); |
| r = sme_mgt_key(priv, &sme_key, CSR_WIFI_SME_LIST_ACTION_ADD); |
| UF_RTNL_LOCK(); |
| if (r) { |
| unifi_error(priv, "SETKEYS request was rejected with result %d\n", r); |
| return convert_sme_error(r); |
| } |
| |
| func_exit(); |
| return r; |
| } /* _unifi_siwencodeext() */ |
| |
| |
| static int |
| unifi_siwencodeext(struct net_device *dev, struct iw_request_info *info, |
| union iwreq_data *wrqu, char *extra) |
| { |
| int err = 0; |
| |
| err = _unifi_siwencodeext(dev, info, wrqu, extra); |
| |
| return err; |
| } /* unifi_siwencodeext() */ |
| |
| |
| static int |
| unifi_giwencodeext(struct net_device *dev, struct iw_request_info *info, |
| union iwreq_data *wrqu, char *extra) |
| { |
| return -EOPNOTSUPP; |
| } /* unifi_giwencodeext() */ |
| |
| |
| /* |
| * --------------------------------------------------------------------------- |
| * unifi_siwpmksa |
| * |
| * SIOCSIWPMKSA - PMKSA cache operation |
| * The caller passes a pmksa structure: |
| * - cmd one of ADD, REMOVE, FLUSH |
| * - bssid MAC address |
| * - pmkid ID string (16 bytes) |
| * |
| * Arguments: |
| * None. |
| * |
| * Returns: |
| * None. |
| * |
| * Notes: |
| * This is not needed since we provide a siwgenie method. |
| * --------------------------------------------------------------------------- |
| */ |
| #define UNIFI_PMKID_KEY_SIZE 16 |
| static int |
| unifi_siwpmksa(struct net_device *dev, struct iw_request_info *info, |
| union iwreq_data *wrqu, char *extra) |
| { |
| netInterface_priv_t *interfacePriv = (netInterface_priv_t *)netdev_priv(dev); |
| unifi_priv_t *priv = interfacePriv->privPtr; |
| struct iw_pmksa *pmksa = (struct iw_pmksa *)extra; |
| CsrResult r = 0; |
| CsrWifiSmePmkidList pmkid_list; |
| CsrWifiSmePmkid pmkid; |
| CsrWifiSmeListAction action; |
| |
| CHECK_INITED(priv); |
| |
| if(interfacePriv->interfaceMode == CSR_WIFI_ROUTER_CTRL_MODE_AP || |
| interfacePriv->interfaceMode == CSR_WIFI_ROUTER_CTRL_MODE_P2PGO) { |
| unifi_error(priv, "unifi_siwpmksa: not permitted in Mode %d\n", |
| interfacePriv->interfaceMode); |
| return -EPERM; |
| } |
| |
| |
| unifi_trace(priv, UDBG1, "SIWPMKSA: cmd %d, %pM\n", pmksa->cmd, |
| pmksa->bssid.sa_data); |
| |
| pmkid_list.pmkids = NULL; |
| switch (pmksa->cmd) { |
| case IW_PMKSA_ADD: |
| pmkid_list.pmkids = &pmkid; |
| action = CSR_WIFI_SME_LIST_ACTION_ADD; |
| pmkid_list.pmkidsCount = 1; |
| memcpy(pmkid.bssid.a, pmksa->bssid.sa_data, ETH_ALEN); |
| memcpy(pmkid.pmkid, pmksa->pmkid, UNIFI_PMKID_KEY_SIZE); |
| break; |
| case IW_PMKSA_REMOVE: |
| pmkid_list.pmkids = &pmkid; |
| action = CSR_WIFI_SME_LIST_ACTION_REMOVE; |
| pmkid_list.pmkidsCount = 1; |
| memcpy(pmkid.bssid.a, pmksa->bssid.sa_data, ETH_ALEN); |
| memcpy(pmkid.pmkid, pmksa->pmkid, UNIFI_PMKID_KEY_SIZE); |
| break; |
| case IW_PMKSA_FLUSH: |
| /* Replace current PMKID's with an empty list */ |
| pmkid_list.pmkidsCount = 0; |
| action = CSR_WIFI_SME_LIST_ACTION_FLUSH; |
| break; |
| default: |
| unifi_notice(priv, "SIWPMKSA: Unknown command (0x%x)\n", pmksa->cmd); |
| return -EINVAL; |
| } |
| |
| /* Set the Value the pmkid's will have 1 added OR 1 removed OR be cleared at this point */ |
| UF_RTNL_UNLOCK(); |
| r = sme_mgt_pmkid(priv, action, &pmkid_list); |
| UF_RTNL_LOCK(); |
| if (r) { |
| unifi_error(priv, "SIWPMKSA: Set PMKID's Failed.\n"); |
| } |
| |
| return r; |
| |
| } /* unifi_siwpmksa() */ |
| |
| |
| /* |
| * --------------------------------------------------------------------------- |
| * unifi_get_wireless_stats |
| * |
| * get_wireless_stats method for Linux wireless extensions. |
| * |
| * Arguments: |
| * dev Pointer to associated netdevice. |
| * |
| * Returns: |
| * Pointer to iw_statistics struct. |
| * --------------------------------------------------------------------------- |
| */ |
| struct iw_statistics * |
| unifi_get_wireless_stats(struct net_device *dev) |
| { |
| netInterface_priv_t *interfacePriv = (netInterface_priv_t *)netdev_priv(dev); |
| unifi_priv_t *priv = interfacePriv->privPtr; |
| |
| if (priv->init_progress != UNIFI_INIT_COMPLETED) { |
| return NULL; |
| } |
| |
| return &priv->wext_wireless_stats; |
| } /* unifi_get_wireless_stats() */ |
| |
| |
| /* |
| * Structures to export the Wireless Handlers |
| */ |
| |
| static const struct iw_priv_args unifi_private_args[] = { |
| /*{ cmd, set_args, get_args, name } */ |
| { SIOCIWS80211POWERSAVEPRIV, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, |
| IW_PRIV_TYPE_NONE, "iwprivs80211ps" }, |
| { SIOCIWG80211POWERSAVEPRIV, IW_PRIV_TYPE_NONE, |
| IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | IWPRIV_POWER_SAVE_MAX_STRING, "iwprivg80211ps" }, |
| { SIOCIWS80211RELOADDEFAULTSPRIV, IW_PRIV_TYPE_NONE, |
| IW_PRIV_TYPE_NONE, "iwprivsdefs" }, |
| { SIOCIWSSMEDEBUGPRIV, IW_PRIV_TYPE_CHAR | IWPRIV_SME_DEBUG_MAX_STRING, IW_PRIV_TYPE_NONE, "iwprivssmedebug" }, |
| #ifdef CSR_WIFI_SECURITY_WAPI_ENABLE |
| { SIOCIWSCONFWAPIPRIV, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, |
| IW_PRIV_TYPE_NONE, "iwprivsconfwapi" }, |
| { SIOCIWSWAPIKEYPRIV, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | sizeof(unifiio_wapi_key_t), |
| IW_PRIV_TYPE_NONE, "iwprivswpikey" }, |
| #endif |
| #ifdef CSR_SUPPORT_WEXT_AP |
| { SIOCIWSAPCFGPRIV, IW_PRIV_TYPE_CHAR | 256, IW_PRIV_TYPE_NONE, "AP_SET_CFG" }, |
| { SIOCIWSAPSTARTPRIV, 0,IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED|IWPRIV_SME_MAX_STRING,"AP_BSS_START" }, |
| { SIOCIWSAPSTOPPRIV, IW_PRIV_TYPE_CHAR |IW_PRIV_SIZE_FIXED|0, |
| IW_PRIV_TYPE_CHAR |IW_PRIV_SIZE_FIXED|0, "AP_BSS_STOP" }, |
| #ifdef ANDROID_BUILD |
| { SIOCIWSFWRELOADPRIV, IW_PRIV_TYPE_CHAR |256, |
| IW_PRIV_TYPE_CHAR |IW_PRIV_SIZE_FIXED|0, "WL_FW_RELOAD" }, |
| { SIOCIWSSTACKSTART, 0, |
| IW_PRIV_TYPE_CHAR |IW_PRIV_SIZE_FIXED|IWPRIV_SME_MAX_STRING, "START" }, |
| { SIOCIWSSTACKSTOP, 0, |
| IW_PRIV_TYPE_CHAR |IW_PRIV_SIZE_FIXED|IWPRIV_SME_MAX_STRING, "STOP" }, |
| #endif /* ANDROID_BUILD */ |
| #endif /* CSR_SUPPORT_WEXT_AP */ |
| }; |
| |
| static const iw_handler unifi_handler[] = |
| { |
| (iw_handler) unifi_siwcommit, /* SIOCSIWCOMMIT */ |
| (iw_handler) unifi_giwname, /* SIOCGIWNAME */ |
| (iw_handler) NULL, /* SIOCSIWNWID */ |
| (iw_handler) NULL, /* SIOCGIWNWID */ |
| (iw_handler) unifi_siwfreq, /* SIOCSIWFREQ */ |
| (iw_handler) unifi_giwfreq, /* SIOCGIWFREQ */ |
| (iw_handler) unifi_siwmode, /* SIOCSIWMODE */ |
| (iw_handler) unifi_giwmode, /* SIOCGIWMODE */ |
| (iw_handler) NULL, /* SIOCSIWSENS */ |
| (iw_handler) NULL, /* SIOCGIWSENS */ |
| (iw_handler) NULL, /* SIOCSIWRANGE */ |
| (iw_handler) unifi_giwrange, /* SIOCGIWRANGE */ |
| (iw_handler) NULL, /* SIOCSIWPRIV */ |
| (iw_handler) NULL, /* SIOCGIWPRIV */ |
| (iw_handler) NULL, /* SIOCSIWSTATS */ |
| (iw_handler) NULL, /* SIOCGIWSTATS */ |
| (iw_handler) NULL, /* SIOCSIWSPY */ |
| (iw_handler) NULL, /* SIOCGIWSPY */ |
| (iw_handler) NULL, /* SIOCSIWTHRSPY */ |
| (iw_handler) NULL, /* SIOCGIWTHRSPY */ |
| (iw_handler) unifi_siwap, /* SIOCSIWAP */ |
| (iw_handler) unifi_giwap, /* SIOCGIWAP */ |
| #if WIRELESS_EXT > 17 |
| /* WPA : IEEE 802.11 MLME requests */ |
| unifi_siwmlme, /* SIOCSIWMLME, request MLME operation */ |
| #else |
| (iw_handler) NULL, /* -- hole -- */ |
| #endif |
| (iw_handler) NULL, /* SIOCGIWAPLIST */ |
| (iw_handler) unifi_siwscan, /* SIOCSIWSCAN */ |
| (iw_handler) unifi_giwscan, /* SIOCGIWSCAN */ |
| (iw_handler) unifi_siwessid, /* SIOCSIWESSID */ |
| (iw_handler) unifi_giwessid, /* SIOCGIWESSID */ |
| (iw_handler) NULL, /* SIOCSIWNICKN */ |
| (iw_handler) NULL, /* SIOCGIWNICKN */ |
| (iw_handler) NULL, /* -- hole -- */ |
| (iw_handler) NULL, /* -- hole -- */ |
| unifi_siwrate, /* SIOCSIWRATE */ |
| unifi_giwrate, /* SIOCGIWRATE */ |
| unifi_siwrts, /* SIOCSIWRTS */ |
| unifi_giwrts, /* SIOCGIWRTS */ |
| unifi_siwfrag, /* SIOCSIWFRAG */ |
| unifi_giwfrag, /* SIOCGIWFRAG */ |
| (iw_handler) NULL, /* SIOCSIWTXPOW */ |
| (iw_handler) NULL, /* SIOCGIWTXPOW */ |
| (iw_handler) NULL, /* SIOCSIWRETRY */ |
| (iw_handler) NULL, /* SIOCGIWRETRY */ |
| unifi_siwencode, /* SIOCSIWENCODE */ |
| unifi_giwencode, /* SIOCGIWENCODE */ |
| unifi_siwpower, /* SIOCSIWPOWER */ |
| unifi_giwpower, /* SIOCGIWPOWER */ |
| #if WIRELESS_EXT > 17 |
| (iw_handler) NULL, /* -- hole -- */ |
| (iw_handler) NULL, /* -- hole -- */ |
| |
| /* WPA : Generic IEEE 802.11 informatiom element (e.g., for WPA/RSN/WMM). */ |
| unifi_siwgenie, /* SIOCSIWGENIE */ /* set generic IE */ |
| unifi_giwgenie, /* SIOCGIWGENIE */ /* get generic IE */ |
| |
| /* WPA : Authentication mode parameters */ |
| unifi_siwauth, /* SIOCSIWAUTH */ /* set authentication mode params */ |
| unifi_giwauth, /* SIOCGIWAUTH */ /* get authentication mode params */ |
| |
| /* WPA : Extended version of encoding configuration */ |
| unifi_siwencodeext, /* SIOCSIWENCODEEXT */ /* set encoding token & mode */ |
| unifi_giwencodeext, /* SIOCGIWENCODEEXT */ /* get encoding token & mode */ |
| |
| /* WPA2 : PMKSA cache management */ |
| unifi_siwpmksa, /* SIOCSIWPMKSA */ /* PMKSA cache operation */ |
| (iw_handler) NULL, /* -- hole -- */ |
| #endif /* WIRELESS_EXT > 17 */ |
| }; |
| |
| |
| static const iw_handler unifi_private_handler[] = |
| { |
| iwprivs80211ps, /* SIOCIWFIRSTPRIV */ |
| iwprivg80211ps, /* SIOCIWFIRSTPRIV + 1 */ |
| iwprivsdefs, /* SIOCIWFIRSTPRIV + 2 */ |
| (iw_handler) NULL, |
| #ifdef CSR_WIFI_SECURITY_WAPI_ENABLE |
| iwprivsconfwapi, /* SIOCIWFIRSTPRIV + 4 */ |
| (iw_handler) NULL, /* SIOCIWFIRSTPRIV + 5 */ |
| iwprivswpikey, /* SIOCIWFIRSTPRIV + 6 */ |
| #else |
| (iw_handler) NULL, |
| (iw_handler) NULL, |
| (iw_handler) NULL, |
| #endif |
| (iw_handler) NULL, |
| iwprivssmedebug, /* SIOCIWFIRSTPRIV + 8 */ |
| #ifdef CSR_SUPPORT_WEXT_AP |
| (iw_handler) NULL, |
| iwprivsapconfig, |
| (iw_handler) NULL, |
| iwprivsapstart, |
| (iw_handler) NULL, |
| iwprivsapstop, |
| (iw_handler) NULL, |
| #ifdef ANDROID_BUILD |
| iwprivsapfwreload, |
| (iw_handler) NULL, |
| iwprivsstackstart, |
| (iw_handler) NULL, |
| iwprivsstackstop, |
| #else |
| (iw_handler) NULL, |
| (iw_handler) NULL, |
| (iw_handler) NULL, |
| (iw_handler) NULL, |
| (iw_handler) NULL, |
| #endif /* ANDROID_BUILD */ |
| #else |
| (iw_handler) NULL, |
| (iw_handler) NULL, |
| (iw_handler) NULL, |
| (iw_handler) NULL, |
| (iw_handler) NULL, |
| (iw_handler) NULL, |
| (iw_handler) NULL, |
| (iw_handler) NULL, |
| (iw_handler) NULL, |
| (iw_handler) NULL, |
| (iw_handler) NULL, |
| (iw_handler) NULL, |
| #endif /* CSR_SUPPORT_WEXT_AP */ |
| }; |
| |
| struct iw_handler_def unifi_iw_handler_def = |
| { |
| .num_standard = sizeof(unifi_handler) / sizeof(iw_handler), |
| .num_private = sizeof(unifi_private_handler) / sizeof(iw_handler), |
| .num_private_args = sizeof(unifi_private_args) / sizeof(struct iw_priv_args), |
| .standard = (iw_handler *) unifi_handler, |
| .private = (iw_handler *) unifi_private_handler, |
| .private_args = (struct iw_priv_args *) unifi_private_args, |
| #if IW_HANDLER_VERSION >= 6 |
| .get_wireless_stats = unifi_get_wireless_stats, |
| #endif |
| }; |
| |
| |