/*
 * ---------------------------------------------------------------------------
 * 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;

    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);
    }

    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;

    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);
    }

    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;

    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);
    }

    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;

    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;

    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;

    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);

    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;

    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);
    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;

    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);
            return convert_sme_error(err);
        }
    }

    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;

    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);
    }

    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 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

    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;
    }


#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);
    }

    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;

    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);
        return convert_sme_error(err);
    }

    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;

    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);
    }


    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;

    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;
    }


    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;

    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;

    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;

    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;
    }

    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;

    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:
            return -EOPNOTSUPP;
    }

    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;

    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) {
        return 0;
    }

    priv->connection_config.mlmeAssociateReqInformationElements = kmalloc(len, GFP_KERNEL);
    if (priv->connection_config.mlmeAssociateReqInformationElements == NULL) {
        return -ENOMEM;
    }

    priv->connection_config.mlmeAssociateReqInformationElementsLength = len;
    memcpy( priv->connection_config.mlmeAssociateReqInformationElements, extra, len);

    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;

    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);

    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;

    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);

    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;

    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);
    }

    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
};