blob: 2814e0bc59a3f0bf16e0b4f83e409325f6bae86a [file] [log] [blame]
/** @file moal_uap_wext.c
*
* @brief This file contains wireless extension standard ioctl functions
*
* Copyright (C) 2010-2014, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
* (the "License"). You may use, redistribute and/or modify this File in
* accordance with the terms and conditions of the License, a copy of which
* is available by writing to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
* worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
*
* THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
* IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
* ARE EXPRESSLY DISCLAIMED. The License provides additional details about
* this warranty disclaimer.
*
*/
/************************************************************************
Change log:
08/06/2010: initial version
************************************************************************/
#include "moal_main.h"
#include "moal_uap.h"
#include "moal_wext.h"
#include "moal_uap_priv.h"
/********************************************************
Global Variables
********************************************************/
typedef struct _chan_to_freq_t {
/** Channel */
t_u16 channel;
/** Frequency */
t_u32 freq;
/** Band */
t_u8 band;
} chan_to_freq_t;
const chan_to_freq_t chan_to_freq[] = {
{1, 2412, 0},
{2, 2417, 0},
{3, 2422, 0},
{4, 2427, 0},
{5, 2432, 0},
{6, 2437, 0},
{7, 2442, 0},
{8, 2447, 0},
{9, 2452, 0},
{10, 2457, 0},
{11, 2462, 0},
{12, 2467, 0},
{13, 2472, 0},
{14, 2484, 0},
{183, 4915, 1},
{184, 4920, 1},
{185, 4925, 1},
{187, 4935, 1},
{188, 4940, 1},
{189, 4945, 1},
{192, 4960, 1},
{196, 4980, 1},
{7, 5035, 1},
{8, 5040, 1},
{9, 5045, 1},
{11, 5055, 1},
{12, 5060, 1},
{16, 5080, 1},
{34, 5170, 1},
{36, 5180, 1},
{38, 5190, 1},
{40, 5200, 1},
{42, 5210, 1},
{44, 5220, 1},
{46, 5230, 1},
{48, 5240, 1},
{52, 5260, 1},
{56, 5280, 1},
{60, 5300, 1},
{64, 5320, 1},
{100, 5500, 1},
{104, 5520, 1},
{108, 5540, 1},
{112, 5560, 1},
{116, 5580, 1},
{120, 5600, 1},
{124, 5620, 1},
{128, 5640, 1},
{132, 5660, 1},
{136, 5680, 1},
{140, 5700, 1},
{149, 5745, 1},
{153, 5765, 1},
{157, 5785, 1},
{161, 5805, 1},
{165, 5825, 1},
};
/** Convertion from frequency to channel */
#define freq_to_chan(x) ((((x) - 2412) / 5) + 1)
/********************************************************
Local Functions
********************************************************/
/**
* @brief Sort Channels
*
* @param freq A pointer to iw_freq structure
* @param num Number of Channels
*
* @return N/A
*/
static inline void
woal_sort_channels(struct iw_freq *freq, int num)
{
int i, j;
struct iw_freq temp;
for (i = 0; i < num; i++)
for (j = i + 1; j < num; j++)
if (freq[i].i > freq[j].i) {
temp.i = freq[i].i;
temp.m = freq[i].m;
freq[i].i = freq[j].i;
freq[i].m = freq[j].m;
freq[j].i = temp.i;
freq[j].m = temp.m;
}
}
/**
* @brief Get frequency for channel in given band
*
* @param channel channel
* @param band band
*
* @return freq
*/
static int
channel_to_frequency(t_u16 channel, t_u8 band)
{
int i = 0;
ENTER();
for (i = 0; i < ARRAY_SIZE(chan_to_freq); i++) {
if (channel == chan_to_freq[i].channel &&
band == chan_to_freq[i].band) {
LEAVE();
return chan_to_freq[i].freq;
}
}
LEAVE();
return 0;
}
/**
* @brief Commit handler: called after a bunch of SET operations
*
* @param dev A pointer to net_device structure
* @param info A pointer to iw_request_info structure
* @param cwrq A pointer to char buffer
* @param extra A pointer to extra data buf
*
* @return 0 --success
*/
static int
woal_config_commit(struct net_device *dev,
struct iw_request_info *info, char *cwrq, char *extra)
{
ENTER();
LEAVE();
return 0;
}
/**
* @brief Get name
*
* @param dev A pointer to net_device structure
* @param info A pointer to iw_request_info structure
* @param cwrq A pointer to char buffer
* @param extra A pointer to extra data buf
*
* @return 0 --success
*/
static int
woal_get_name(struct net_device *dev, struct iw_request_info *info,
char *cwrq, char *extra)
{
ENTER();
strcpy(cwrq, "IEEE 802.11-DS");
LEAVE();
return 0;
}
/**
* @brief Get current BSSID
*
* @param dev A pointer to net_device structure
* @param info A pointer to iw_request_info structure
* @param awrq A pointer to sockaddr structure
* @param extra A pointer to extra data buf
*
* @return 0 --success
*/
static int
woal_get_wap(struct net_device *dev, struct iw_request_info *info,
struct sockaddr *awrq, char *extra)
{
moal_private *priv = (moal_private *) netdev_priv(dev);
int ret = 0;
ENTER();
if (priv->bss_started)
memcpy(awrq->sa_data, priv->current_addr, MLAN_MAC_ADDR_LENGTH);
else
memset(awrq->sa_data, 0, MLAN_MAC_ADDR_LENGTH);
awrq->sa_family = ARPHRD_ETHER;
LEAVE();
return ret;
}
/**
* @brief Change the AP BSSID
*
* @param dev A pointer to net_device structure
* @param info A pointer to iw_request_info structure
* @param awrq A pointer to iw_param structure
* @param extra A pointer to extra data buf
*
* @return 0 --success, otherwise fail
*/
static int
woal_set_wap(struct net_device *dev, struct iw_request_info *info,
struct sockaddr *awrq, char *extra)
{
int ret = 0;
moal_private *priv = (moal_private *) netdev_priv(dev);
const t_u8 zero_mac[MLAN_MAC_ADDR_LENGTH] = { 0, 0, 0, 0, 0, 0 };
ENTER();
if (awrq->sa_family != ARPHRD_ETHER) {
ret = -EINVAL;
goto done;
}
PRINTM(MINFO, "ASSOC: WAP: uAP bss : " MACSTR "\n",
MAC2STR((t_u8 *) awrq->sa_data));
/*
* Using this ioctl to start/stop the BSS, return if bss
* is already started/stopped.
*/
if (memcmp(zero_mac, awrq->sa_data, MLAN_MAC_ADDR_LENGTH)) {
if (priv->bss_started == MFALSE)
ret = woal_uap_bss_ctrl(priv, MOAL_IOCTL_WAIT,
UAP_BSS_START);
else
PRINTM(MINFO, "BSS is already started.\n");
} else {
/* zero_mac means bss_stop */
if (priv->bss_started == MTRUE)
ret = woal_uap_bss_ctrl(priv, MOAL_IOCTL_WAIT,
UAP_BSS_STOP);
else
PRINTM(MINFO, "BSS is already stopped.\n");
}
done:
LEAVE();
return ret;
}
/**
* @brief Set frequency/channel
*
* @param dev A pointer to net_device structure
* @param info A pointer to iw_request_info structure
* @param fwrq A pointer to iw_freq structure
* @param extra A pointer to extra data buf
*
* @return 0 --success, otherwise fail
*/
static int
woal_set_freq(struct net_device *dev, struct iw_request_info *info,
struct iw_freq *fwrq, char *extra)
{
moal_private *priv = (moal_private *) netdev_priv(dev);
mlan_uap_bss_param *sys_cfg = NULL, *ap_cfg = NULL;
int ret = 0, chan = 0, i = 0;
ENTER();
ap_cfg = kmalloc(sizeof(mlan_uap_bss_param), GFP_KERNEL);
if (ap_cfg == NULL) {
ret = -ENOMEM;
goto done;
}
sys_cfg = kmalloc(sizeof(mlan_uap_bss_param), GFP_KERNEL);
if (sys_cfg == NULL) {
ret = -ENOMEM;
goto done;
}
if (MLAN_STATUS_SUCCESS != woal_set_get_sys_config(priv,
MLAN_ACT_GET,
MOAL_IOCTL_WAIT,
ap_cfg)) {
PRINTM(MERROR, "Error getting AP confiruration\n");
ret = -EFAULT;
goto done;
}
i = ap_cfg->num_of_chan;
/* Initialize the invalid values so that the correct values below are
downloaded to firmware */
woal_set_sys_config_invalid_data(sys_cfg);
/* If setting by frequency, convert to a channel */
if (fwrq->e == 1)
chan = freq_to_chan(fwrq->m / 100000);
else
chan = fwrq->m;
if (chan > 0 && chan < MLAN_MAX_CHANNEL)
sys_cfg->channel = chan;
else {
ret = -EINVAL;
goto done;
}
for (i = 0; i < ap_cfg->num_of_chan; i++)
if (ap_cfg->chan_list[i].chan_number == chan)
break;
if (i == ap_cfg->num_of_chan) {
PRINTM(MERROR, "Channel %d is not supported\n", chan);
ret = -EINVAL;
goto done;
}
if (MLAN_STATUS_SUCCESS != woal_set_get_sys_config(priv,
MLAN_ACT_SET,
MOAL_IOCTL_WAIT,
sys_cfg)) {
PRINTM(MERROR, "Error setting AP confiruration\n");
ret = -EFAULT;
goto done;
}
done:
kfree(sys_cfg);
kfree(ap_cfg);
LEAVE();
return ret;
}
/**
* @brief Get frequency and channel
*
* @param dev A pointer to net_device structure
* @param info A pointer to iw_request_info structure
* @param fwrq A pointer to iw_freq structure
* @param extra A pointer to extra data buf
*
* @return 0 --success, otherwise fail
*/
static int
woal_get_freq(struct net_device *dev, struct iw_request_info *info,
struct iw_freq *fwrq, char *extra)
{
moal_private *priv = (moal_private *) netdev_priv(dev);
mlan_uap_bss_param ap_cfg;
t_u8 band = 0;
int ret = 0;
ENTER();
if (MLAN_STATUS_SUCCESS != woal_set_get_sys_config(priv,
MLAN_ACT_GET,
MOAL_IOCTL_WAIT,
&ap_cfg)) {
PRINTM(MERROR, "Error getting AP confiruration\n");
LEAVE();
return -EFAULT;
}
band = ap_cfg.band_cfg & BAND_CONFIG_5GHZ;
fwrq->m = (long)channel_to_frequency(ap_cfg.channel, band);
fwrq->i = (long)ap_cfg.channel;
fwrq->e = 6;
LEAVE();
return ret;
}
/**
* @brief Set wlan mode
*
* @param dev A pointer to net_device structure
* @param info A pointer to iw_request_info structure
* @param uwrq A pointer to t_u32 string
* @param extra A pointer to extra data buf
*
* @return 0 --success, otherwise fail
*/
static int
woal_set_bss_mode(struct net_device *dev, struct iw_request_info *info,
t_u32 * uwrq, char *extra)
{
int ret = 0;
ENTER();
switch (*uwrq) {
case IW_MODE_AUTO:
case IW_MODE_MASTER:
PRINTM(MINFO, "This is correct mode in AP mode\n");
break;
default:
PRINTM(MERROR, "Invalid mode for AP\n");
ret = -EINVAL;
}
LEAVE();
return ret;
}
/**
* @brief Get wlan mode
*
* @param dev A pointer to net_device structure
* @param info A pointer to iw_request_info structure
* @param uwrq A pointer to t_u32 string
* @param extra A pointer to extra data buf
*
* @return 0 --success
*/
static int
woal_get_bss_mode(struct net_device *dev, struct iw_request_info *info,
t_u32 * uwrq, char *extra)
{
ENTER();
*uwrq = IW_MODE_MASTER;
LEAVE();
return 0;
}
/**
* @brief Set encryption key
*
* @param dev A pointer to net_device structure
* @param info A pointer to iw_request_info structure
* @param dwrq A pointer to iw_point structure
* @param extra A pointer to extra data buf
*
* @return 0 --success, otherwise fail
*/
static int
woal_set_encode(struct net_device *dev, struct iw_request_info *info,
struct iw_point *dwrq, char *extra)
{
int ret = 0;
moal_private *priv = (moal_private *) netdev_priv(dev);
mlan_uap_bss_param *sys_cfg = NULL, *ap_cfg = NULL;
wep_key *pkey = NULL;
int key_index = 0;
ENTER();
/* Check index */
key_index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
if (key_index > 3) {
PRINTM(MERROR, "Key index #%d out of range\n", key_index);
ret = -EINVAL;
goto done;
}
ap_cfg = kmalloc(sizeof(mlan_uap_bss_param), GFP_KERNEL);
if (ap_cfg == NULL) {
ret = -ENOMEM;
goto done;
}
sys_cfg = kmalloc(sizeof(mlan_uap_bss_param), GFP_KERNEL);
if (sys_cfg == NULL) {
ret = -ENOMEM;
goto done;
}
if (MLAN_STATUS_SUCCESS != woal_set_get_sys_config(priv,
MLAN_ACT_GET,
MOAL_IOCTL_WAIT,
ap_cfg)) {
PRINTM(MERROR, "Error getting AP confiruration\n");
ret = -EFAULT;
goto done;
}
/* Initialize the invalid values so that the correct values below are
downloaded to firmware */
woal_set_sys_config_invalid_data(sys_cfg);
sys_cfg->wep_cfg.key0.key_index = 0;
sys_cfg->wep_cfg.key1.key_index = 1;
sys_cfg->wep_cfg.key2.key_index = 2;
sys_cfg->wep_cfg.key3.key_index = 3;
if (key_index >= 0 && key_index <= 3) {
if (key_index == 0)
pkey = &sys_cfg->wep_cfg.key0;
else if (key_index == 1)
pkey = &sys_cfg->wep_cfg.key1;
else if (key_index == 2)
pkey = &sys_cfg->wep_cfg.key2;
else if (key_index == 3)
pkey = &sys_cfg->wep_cfg.key3;
}
if (!(dwrq->flags & IW_ENCODE_NOKEY) && dwrq->length) {
if (dwrq->length > MAX_WEP_KEY_SIZE) {
PRINTM(MERROR, "Key length (%d) out of range\n",
dwrq->length);
ret = -E2BIG;
goto done;
}
if (key_index < 0) {
/* Get current default key index */
if (ap_cfg->wep_cfg.key0.is_default)
pkey = &sys_cfg->wep_cfg.key0;
if (ap_cfg->wep_cfg.key1.is_default)
pkey = &sys_cfg->wep_cfg.key1;
if (ap_cfg->wep_cfg.key2.is_default)
pkey = &sys_cfg->wep_cfg.key2;
if (ap_cfg->wep_cfg.key3.is_default)
pkey = &sys_cfg->wep_cfg.key3;
else { /* Something wrong, select first key as default
*/
PRINTM(MERROR,
"No default key set! Selecting first key.\n");
pkey = &sys_cfg->wep_cfg.key0;
}
}
sys_cfg->protocol = PROTOCOL_STATIC_WEP;
if (extra)
memcpy(pkey->key, extra, dwrq->length);
/* Set the length */
if (dwrq->length > MIN_WEP_KEY_SIZE)
pkey->length = MAX_WEP_KEY_SIZE;
else
pkey->length = MIN_WEP_KEY_SIZE;
/* Set current key index as default */
pkey->is_default = MTRUE;
} else {
/*
* No key provided so it is either enable key,
* on or off
*/
if (dwrq->flags & IW_ENCODE_DISABLED) {
PRINTM(MINFO, "*** iwconfig mlanX key off ***\n");
sys_cfg->protocol = PROTOCOL_NO_SECURITY;
} else {
/*
* iwconfig mlanX key [n]
* iwconfig mlanX key on
* Do we want to just set the transmit key index ?
*/
if (key_index < 0) {
PRINTM(MINFO,
"*** iwconfig mlanX key on ***\n");
} else {
/* Get current key configuration at key_index */
if (key_index == 0)
memcpy(pkey, &ap_cfg->wep_cfg.key0,
sizeof(wep_key));
if (key_index == 1)
memcpy(pkey, &ap_cfg->wep_cfg.key1,
sizeof(wep_key));
if (key_index == 2)
memcpy(pkey, &ap_cfg->wep_cfg.key2,
sizeof(wep_key));
if (key_index == 3)
memcpy(pkey, &ap_cfg->wep_cfg.key3,
sizeof(wep_key));
/* Set current key index as default */
pkey->is_default = MTRUE;
}
}
}
if (dwrq->flags & (IW_ENCODE_RESTRICTED | IW_ENCODE_OPEN)) {
switch (dwrq->flags & 0xf000) {
case IW_ENCODE_RESTRICTED:
/* iwconfig mlanX restricted key [1] */
sys_cfg->auth_mode = MLAN_AUTH_MODE_SHARED;
PRINTM(MINFO, "Auth mode restricted!\n");
break;
case IW_ENCODE_OPEN:
/* iwconfig mlanX key [2] open */
sys_cfg->auth_mode = MLAN_AUTH_MODE_OPEN;
PRINTM(MINFO, "Auth mode open!\n");
break;
case IW_ENCODE_RESTRICTED | IW_ENCODE_OPEN:
default:
/* iwconfig mlanX key [2] open restricted */
PRINTM(MINFO, "Auth mode auto!\n");
break;
}
}
if (MLAN_STATUS_SUCCESS != woal_set_get_sys_config(priv,
MLAN_ACT_SET,
MOAL_IOCTL_WAIT,
sys_cfg)) {
PRINTM(MERROR, "Error setting AP confiruration\n");
ret = -EFAULT;
goto done;
}
done:
kfree(sys_cfg);
kfree(ap_cfg);
LEAVE();
return ret;
}
/**
* @brief Get encryption key
*
* @param dev A pointer to net_device structure
* @param info A pointer to iw_request_info structure
* @param dwrq A pointer to iw_point structure
* @param extra A pointer to extra data buf
*
* @return 0 --success, otherwise fail
*/
static int
woal_get_encode(struct net_device *dev, struct iw_request_info *info,
struct iw_point *dwrq, char *extra)
{
moal_private *priv = (moal_private *) netdev_priv(dev);
int index = (dwrq->flags & IW_ENCODE_INDEX);
wep_key *pkey = NULL;
mlan_uap_bss_param ap_cfg;
int ret = 0;
ENTER();
if (index < 0 || index > 4) {
PRINTM(MERROR, "Key index #%d out of range\n", index);
ret = -EINVAL;
goto done;
}
if (MLAN_STATUS_SUCCESS != woal_set_get_sys_config(priv,
MLAN_ACT_GET,
MOAL_IOCTL_WAIT,
&ap_cfg)) {
PRINTM(MERROR, "Error getting AP confiruration\n");
ret = -EFAULT;
goto done;
}
dwrq->flags = 0;
/*
* Check encryption mode
*/
switch (ap_cfg.auth_mode) {
case MLAN_AUTH_MODE_OPEN:
dwrq->flags = IW_ENCODE_OPEN;
break;
case MLAN_AUTH_MODE_SHARED:
case MLAN_AUTH_MODE_NETWORKEAP:
dwrq->flags = IW_ENCODE_RESTRICTED;
break;
default:
dwrq->flags = IW_ENCODE_DISABLED | IW_ENCODE_OPEN;
break;
}
switch (ap_cfg.protocol) {
case PROTOCOL_NO_SECURITY:
dwrq->flags |= IW_ENCODE_DISABLED;
break;
case PROTOCOL_STATIC_WEP:
if (ap_cfg.wep_cfg.key0.is_default)
pkey = &ap_cfg.wep_cfg.key0;
else if (ap_cfg.wep_cfg.key1.is_default)
pkey = &ap_cfg.wep_cfg.key1;
else if (ap_cfg.wep_cfg.key2.is_default)
pkey = &ap_cfg.wep_cfg.key2;
else if (ap_cfg.wep_cfg.key3.is_default)
pkey = &ap_cfg.wep_cfg.key3;
if (pkey) {
dwrq->flags |= (pkey->key_index + 1);
dwrq->length = pkey->length;
memcpy(extra, pkey->key, pkey->length);
dwrq->flags &= ~IW_ENCODE_DISABLED;
} else {
ret = -EFAULT;
}
break;
case PROTOCOL_WPA:
case PROTOCOL_WPA2:
case PROTOCOL_WPA2_MIXED:
memcpy(extra, ap_cfg.wpa_cfg.passphrase, ap_cfg.wpa_cfg.length);
dwrq->length = ap_cfg.wpa_cfg.length;
dwrq->flags |= 1;
dwrq->flags &= ~IW_ENCODE_DISABLED;
break;
default:
dwrq->flags &= ~IW_ENCODE_DISABLED;
break;
}
dwrq->flags |= IW_ENCODE_NOKEY;
done:
LEAVE();
return ret;
}
#if (WIRELESS_EXT >= 18)
/**
* @brief Get IE
*
* @param dev A pointer to net_device structure
* @param info A pointer to iw_request_info structure
* @param dwrq A pointer to iw_point structure
* @param extra A pointer to extra data buf
*
* @return -EOPNOTSUPP
*/
static int
woal_get_gen_ie(struct net_device *dev, struct iw_request_info *info,
struct iw_point *dwrq, char *extra)
{
ENTER();
LEAVE();
return -EOPNOTSUPP;
}
/**
* @brief Set IE
*
* Pass an opaque block of data, expected to be IEEE IEs, to the driver
* for eventual passthrough to the firmware in an associate/join
* (and potentially start) command.
*
* @param dev A pointer to net_device structure
* @param info A pointer to iw_request_info structure
* @param dwrq A pointer to iw_point structure
* @param extra A pointer to extra data buf
*
* @return 0 --success, otherwise fail
*/
static int
woal_set_gen_ie(struct net_device *dev, struct iw_request_info *info,
struct iw_point *dwrq, char *extra)
{
moal_private *priv = (moal_private *) netdev_priv(dev);
mlan_uap_bss_param sys_cfg;
IEEEtypes_Header_t *tlv = NULL;
int tlv_hdr_len = sizeof(IEEEtypes_Header_t), tlv_buf_left = 0;
int ret = 0;
ENTER();
/* Initialize the invalid values so that the correct values below are
downloaded to firmware */
woal_set_sys_config_invalid_data(&sys_cfg);
tlv_buf_left = dwrq->length;
tlv = (IEEEtypes_Header_t *) extra;
while (tlv_buf_left >= tlv_hdr_len) {
if (tlv->element_id == WPA_IE) {
sys_cfg.protocol |= PROTOCOL_WPA;
if (priv->pairwise_cipher == CIPHER_TKIP) {
sys_cfg.wpa_cfg.pairwise_cipher_wpa =
CIPHER_TKIP;
PRINTM(MINFO, "Set IE Cipher TKIP\n");
}
if (priv->pairwise_cipher == CIPHER_AES_CCMP) {
sys_cfg.wpa_cfg.pairwise_cipher_wpa =
CIPHER_AES_CCMP;
PRINTM(MINFO, "Set IE Cipher CCMP\n");
}
if (priv->pairwise_cipher ==
(CIPHER_TKIP | CIPHER_AES_CCMP)) {
sys_cfg.wpa_cfg.pairwise_cipher_wpa =
CIPHER_TKIP | CIPHER_AES_CCMP;
PRINTM(MINFO, "Set IE Cipher TKIP + CCMP\n");
}
memcpy(priv->bcn_ie_buf + priv->bcn_ie_len,
((t_u8 *) tlv),
sizeof(IEEEtypes_Header_t) + tlv->len);
priv->bcn_ie_len +=
sizeof(IEEEtypes_Header_t) + tlv->len;
}
if (tlv->element_id == RSN_IE) {
sys_cfg.protocol |= PROTOCOL_WPA2;
if (priv->pairwise_cipher == CIPHER_TKIP) {
sys_cfg.wpa_cfg.pairwise_cipher_wpa2 =
CIPHER_TKIP;
}
if (priv->pairwise_cipher == CIPHER_AES_CCMP) {
sys_cfg.wpa_cfg.pairwise_cipher_wpa2 =
CIPHER_AES_CCMP;
}
if (priv->pairwise_cipher ==
(CIPHER_TKIP | CIPHER_AES_CCMP)) {
sys_cfg.wpa_cfg.pairwise_cipher_wpa2 =
(CIPHER_TKIP | CIPHER_AES_CCMP);
}
memcpy(priv->bcn_ie_buf + priv->bcn_ie_len,
((t_u8 *) tlv),
sizeof(IEEEtypes_Header_t) + tlv->len);
priv->bcn_ie_len +=
sizeof(IEEEtypes_Header_t) + tlv->len;
}
if (priv->group_cipher == CIPHER_TKIP)
sys_cfg.wpa_cfg.group_cipher = CIPHER_TKIP;
if (priv->group_cipher == CIPHER_AES_CCMP)
sys_cfg.wpa_cfg.group_cipher = CIPHER_AES_CCMP;
tlv_buf_left -= (tlv_hdr_len + tlv->len);
tlv = (IEEEtypes_Header_t *) ((t_u8 *) tlv + tlv_hdr_len +
tlv->len);
}
sys_cfg.key_mgmt = priv->uap_key_mgmt;
if (sys_cfg.key_mgmt & KEY_MGMT_PSK)
sys_cfg.key_mgmt_operation |= 0x01;
if (sys_cfg.key_mgmt & KEY_MGMT_EAP)
sys_cfg.key_mgmt_operation |= 0x03;
if (sys_cfg.protocol) {
if (MLAN_STATUS_SUCCESS != woal_set_get_sys_config(priv,
MLAN_ACT_SET,
MOAL_IOCTL_WAIT,
&sys_cfg)) {
PRINTM(MERROR, "Error setting AP configuration\n");
ret = -EFAULT;
goto done;
}
priv->pairwise_cipher = 0;
priv->group_cipher = 0;
/* custom IE command to set priv->bcn_ie_buf */
if (MLAN_STATUS_SUCCESS !=
#define UAP_RSN_MASK (BIT(8) | BIT(5) | BIT(1) | BIT(3))
woal_set_get_custom_ie(priv, UAP_RSN_MASK, priv->bcn_ie_buf,
priv->bcn_ie_len)) {
PRINTM(MERROR, "Error setting wpa-rsn IE\n");
ret = -EFAULT;
}
} else if (dwrq->length == 0) {
/* custom IE command to re-set priv->bcn_ie_buf */
if (MLAN_STATUS_SUCCESS !=
woal_set_get_custom_ie(priv, 0, priv->bcn_ie_buf,
priv->bcn_ie_len)) {
PRINTM(MERROR, "Error resetting wpa-rsn IE\n");
ret = -EFAULT;
}
priv->bcn_ie_len = 0;
}
done:
LEAVE();
return ret;
}
/**
* @brief Extended version of encoding configuration
*
* @param dev A pointer to net_device structure
* @param info A pointer to iw_request_info structure
* @param dwrq A pointer to iw_point structure
* @param extra A pointer to extra data buf
*
* @return 0 --success, otherwise fail
*/
static int
woal_set_encode_ext(struct net_device *dev,
struct iw_request_info *info,
struct iw_point *dwrq, char *extra)
{
struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
moal_private *priv = (moal_private *) netdev_priv(dev);
int key_index;
t_u8 *pkey_material = NULL;
mlan_ioctl_req *req = NULL;
mlan_ds_sec_cfg *sec = NULL;
mlan_uap_bss_param sys_cfg;
wep_key *pwep_key = NULL;
int ret = 0;
mlan_status status = MLAN_STATUS_SUCCESS;
ENTER();
key_index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
if (key_index < 0 || key_index > 5) {
ret = -EINVAL;
goto done;
}
if (ext->key_len > (dwrq->length - sizeof(struct iw_encode_ext))) {
ret = -EINVAL;
goto done;
}
/* Initialize the invalid values so that the correct values below are
downloaded to firmware */
woal_set_sys_config_invalid_data(&sys_cfg);
pkey_material = (t_u8 *) (ext + 1);
/* Disable Key */
if ((dwrq->flags & IW_ENCODE_DISABLED) && !ext->key_len) {
sys_cfg.protocol = PROTOCOL_NO_SECURITY;
} else if (ext->alg == IW_ENCODE_ALG_WEP) {
sys_cfg.protocol = PROTOCOL_STATIC_WEP;
/* Set WEP key */
switch (key_index) {
case 0:
pwep_key = &sys_cfg.wep_cfg.key0;
break;
case 1:
pwep_key = &sys_cfg.wep_cfg.key1;
break;
case 2:
pwep_key = &sys_cfg.wep_cfg.key2;
break;
case 3:
pwep_key = &sys_cfg.wep_cfg.key3;
break;
}
if (pwep_key) {
pwep_key->key_index = key_index;
pwep_key->is_default = MTRUE;
pwep_key->length = ext->key_len;
memcpy(pwep_key->key, pkey_material, ext->key_len);
}
} else {
/* Set GTK/PTK key */
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg));
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
sec = (mlan_ds_sec_cfg *) req->pbuf;
sec->sub_command = MLAN_OID_SEC_CFG_ENCRYPT_KEY;
req->req_id = MLAN_IOCTL_SEC_CFG;
req->action = MLAN_ACT_SET;
sec->param.encrypt_key.key_len = ext->key_len;
sec->param.encrypt_key.key_index = key_index;
memcpy(sec->param.encrypt_key.key_material, pkey_material,
ext->key_len);
memcpy(sec->param.encrypt_key.mac_addr, ext->addr.sa_data,
ETH_ALEN);
sec->param.encrypt_key.key_flags = ext->ext_flags;
if (ext->ext_flags & IW_ENCODE_EXT_RX_SEQ_VALID) {
memcpy(sec->param.encrypt_key.pn, (t_u8 *) ext->rx_seq,
SEQ_MAX_SIZE);
DBG_HEXDUMP(MCMD_D, "Uap Rx PN",
sec->param.encrypt_key.pn, SEQ_MAX_SIZE);
}
if (ext->ext_flags & IW_ENCODE_EXT_TX_SEQ_VALID) {
memcpy(sec->param.encrypt_key.pn, (t_u8 *) ext->tx_seq,
SEQ_MAX_SIZE);
DBG_HEXDUMP(MCMD_D, "Uap Tx PN",
sec->param.encrypt_key.pn, SEQ_MAX_SIZE);
}
PRINTM(MIOCTL,
"set uap wpa key key_index=%d, key_len=%d key_flags=0x%x "
MACSTR "\n", key_index, ext->key_len,
sec->param.encrypt_key.key_flags,
MAC2STR(sec->param.encrypt_key.mac_addr));
DBG_HEXDUMP(MCMD_D, "uap wpa key", pkey_material, ext->key_len);
#define IW_ENCODE_ALG_AES_CMAC 5
if (ext->alg == IW_ENCODE_ALG_AES_CMAC)
sec->param.encrypt_key.key_flags |=
KEY_FLAG_AES_MCAST_IGTK;
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
if (status != MLAN_STATUS_SUCCESS)
ret = -EFAULT;
/* Cipher set will be done in set generic IE */
priv->pairwise_cipher = ext->alg;
priv->group_cipher = ext->alg;
goto done; /* No AP configuration */
}
if (MLAN_STATUS_SUCCESS != woal_set_get_sys_config(priv,
MLAN_ACT_SET,
MOAL_IOCTL_WAIT,
&sys_cfg)) {
PRINTM(MERROR, "Error setting AP confiruration\n");
ret = -EFAULT;
goto done;
}
done:
if (status != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief Extended version of encoding configuration
*
* @param dev A pointer to net_device structure
* @param info A pointer to iw_request_info structure
* @param dwrq A pointer to iw_point structure
* @param extra A pointer to extra data buf
*
* @return -EOPNOTSUPP
*/
static int
woal_get_encode_ext(struct net_device *dev,
struct iw_request_info *info,
struct iw_point *dwrq, char *extra)
{
ENTER();
LEAVE();
return -EOPNOTSUPP;
}
/**
* @brief Request MLME operation
*
* @param dev A pointer to net_device structure
* @param info A pointer to iw_request_info structure
* @param dwrq A pointer to iw_point structure
* @param extra A pointer to extra data buf
*
* @return 0--success, otherwise fail
*/
static int
woal_set_mlme(struct net_device *dev,
struct iw_request_info *info, struct iw_point *dwrq, char *extra)
{
struct iw_mlme *mlme = (struct iw_mlme *)extra;
moal_private *priv = (moal_private *) netdev_priv(dev);
mlan_ds_bss *bss = NULL;
mlan_ds_get_info *pinfo = NULL;
mlan_ioctl_req *req = NULL;
mlan_ds_sta_list *sta_list = NULL;
const t_u8 bc_addr[] = { 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF };
t_u8 sta_addr[ETH_ALEN];
int ret = 0, i;
mlan_status status = MLAN_STATUS_SUCCESS;
ENTER();
memset(sta_addr, 0, ETH_ALEN);
if ((mlme->cmd == IW_MLME_DEAUTH) || (mlme->cmd == IW_MLME_DISASSOC)) {
memcpy(sta_addr, (t_u8 *) mlme->addr.sa_data, ETH_ALEN);
PRINTM(MIOCTL,
"Deauth station: " MACSTR ", reason=%d\n",
MAC2STR(sta_addr), mlme->reason_code);
/* FIXME: For flushing all stations we need to use zero MAC,
but right now the FW does not support this. So, manually
delete each one individually. */
/* If deauth all station, get the connected STA list first */
if (!memcmp(bc_addr, sta_addr, ETH_ALEN)) {
PRINTM(MIOCTL, "Deauth all stations\n");
req = woal_alloc_mlan_ioctl_req(sizeof
(mlan_ds_get_info));
if (req == NULL) {
LEAVE();
return -ENOMEM;
}
pinfo = (mlan_ds_get_info *) req->pbuf;
pinfo->sub_command = MLAN_OID_UAP_STA_LIST;
req->req_id = MLAN_IOCTL_GET_INFO;
req->action = MLAN_ACT_GET;
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
if (status != MLAN_STATUS_SUCCESS) {
ret = -EFAULT;
goto done;
}
sta_list =
kmalloc(sizeof(mlan_ds_sta_list), GFP_KERNEL);
if (sta_list == NULL) {
PRINTM(MERROR, "Memory allocation failed!\n");
ret = -ENOMEM;
goto done;
}
memcpy(sta_list, &pinfo->param.sta_list,
sizeof(mlan_ds_sta_list));
kfree(req);
req = NULL;
}
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss));
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
bss = (mlan_ds_bss *) req->pbuf;
bss->sub_command = MLAN_OID_UAP_DEAUTH_STA;
req->req_id = MLAN_IOCTL_BSS;
req->action = MLAN_ACT_SET;
if (!memcmp(bc_addr, sta_addr, ETH_ALEN)) {
for (i = 0; i < sta_list->sta_count; i++) {
memcpy(bss->param.deauth_param.mac_addr,
sta_list->info[i].mac_address, ETH_ALEN);
bss->param.deauth_param.reason_code =
mlme->reason_code;
status = woal_request_ioctl(priv, req,
MOAL_IOCTL_WAIT);
if (status != MLAN_STATUS_SUCCESS) {
ret = -EFAULT;
goto done;
}
}
} else {
memcpy(bss->param.deauth_param.mac_addr, sta_addr,
ETH_ALEN);
bss->param.deauth_param.reason_code = mlme->reason_code;
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
if (status != MLAN_STATUS_SUCCESS) {
ret = -EFAULT;
goto done;
}
}
}
done:
if (status != MLAN_STATUS_PENDING)
kfree(req);
kfree(sta_list);
LEAVE();
return ret;
}
/** @brief Set authentication mode parameters
*
* @param dev A pointer to net_device structure
* @param info A pointer to iw_request_info structure
* @param vwrq A pointer to iw_param structure
* @param extra A pointer to extra data buf
*
* @return 0 --success, otherwise fail
*/
static int
woal_set_auth(struct net_device *dev, struct iw_request_info *info,
struct iw_param *vwrq, char *extra)
{
int ret = 0;
moal_private *priv = (moal_private *) netdev_priv(dev);
mlan_uap_bss_param sys_cfg;
ENTER();
/* Initialize the invalid values so that the correct values below are
downloaded to firmware */
woal_set_sys_config_invalid_data(&sys_cfg);
switch (vwrq->flags & IW_AUTH_INDEX) {
case IW_AUTH_CIPHER_PAIRWISE:
/* Rest are not supported now */
if (vwrq->value & IW_AUTH_CIPHER_NONE)
/* XXX Do not delete no-operation line */
;
else if (vwrq->value & IW_AUTH_CIPHER_WEP40)
/* XXX Do not delete no-operation line */
;
else if (vwrq->value & IW_AUTH_CIPHER_WEP104)
/* XXX Do not delete no-operation line */
;
else if (vwrq->value == IW_AUTH_CIPHER_TKIP) {
sys_cfg.wpa_cfg.pairwise_cipher_wpa = CIPHER_TKIP;
sys_cfg.wpa_cfg.pairwise_cipher_wpa2 = CIPHER_TKIP;
priv->pairwise_cipher = CIPHER_TKIP;
if (!priv->uap_key_mgmt)
priv->uap_key_mgmt = KEY_MGMT_PSK;
PRINTM(MINFO, "Set Auth Cipher TKIP\n");
} else if (vwrq->value == IW_AUTH_CIPHER_CCMP) {
sys_cfg.wpa_cfg.pairwise_cipher_wpa = CIPHER_AES_CCMP;
sys_cfg.wpa_cfg.pairwise_cipher_wpa2 = CIPHER_AES_CCMP;
priv->pairwise_cipher = CIPHER_AES_CCMP;
if (!priv->uap_key_mgmt)
priv->uap_key_mgmt = KEY_MGMT_PSK;
PRINTM(MINFO, "Set Auth Cipher CCMP\n");
} else if (vwrq->value ==
(IW_AUTH_CIPHER_TKIP | IW_AUTH_CIPHER_CCMP)) {
sys_cfg.wpa_cfg.pairwise_cipher_wpa =
(CIPHER_TKIP | CIPHER_AES_CCMP);
sys_cfg.wpa_cfg.pairwise_cipher_wpa2 =
(CIPHER_TKIP | CIPHER_AES_CCMP);
priv->pairwise_cipher = (CIPHER_TKIP | CIPHER_AES_CCMP);
if (!priv->uap_key_mgmt)
priv->uap_key_mgmt = KEY_MGMT_PSK;
PRINTM(MINFO, "Set Auth Cipher TKIP + CCMP\n");
}
break;
case IW_AUTH_CIPHER_GROUP:
/* Rest are not supported now */
if (vwrq->value & IW_AUTH_CIPHER_NONE)
/* XXX Do not delete no-operation line */
;
else if (vwrq->value & IW_AUTH_CIPHER_WEP40)
/* XXX Do not delete no-operation line */
;
else if (vwrq->value & IW_AUTH_CIPHER_WEP104)
/* XXX Do not delete no-operation line */
;
else if (vwrq->value & IW_AUTH_CIPHER_TKIP) {
sys_cfg.wpa_cfg.group_cipher = CIPHER_TKIP;
priv->group_cipher = CIPHER_TKIP;
if (!priv->uap_key_mgmt)
priv->uap_key_mgmt = KEY_MGMT_PSK;
PRINTM(MINFO, "Set Auth Cipher TKIP\n");
} else if (vwrq->value & IW_AUTH_CIPHER_CCMP) {
sys_cfg.wpa_cfg.group_cipher = CIPHER_AES_CCMP;
priv->group_cipher = CIPHER_AES_CCMP;
if (!priv->uap_key_mgmt)
priv->uap_key_mgmt = KEY_MGMT_PSK;
PRINTM(MINFO, "Set Auth Cipher CCMP\n");
}
break;
case IW_AUTH_80211_AUTH_ALG:
switch (vwrq->value) {
case IW_AUTH_ALG_SHARED_KEY:
PRINTM(MINFO, "Auth mode shared key!\n");
sys_cfg.auth_mode = MLAN_AUTH_MODE_SHARED;
break;
case IW_AUTH_ALG_LEAP:
break;
case IW_AUTH_ALG_OPEN_SYSTEM:
PRINTM(MINFO, "Auth mode open!\n");
sys_cfg.auth_mode = MLAN_AUTH_MODE_OPEN;
break;
default:
PRINTM(MINFO, "Auth mode auto!\n");
break;
}
break;
case IW_AUTH_WPA_VERSION:
switch (vwrq->value) {
case IW_AUTH_WPA_VERSION_DISABLED:
sys_cfg.protocol = PROTOCOL_NO_SECURITY;
break;
case IW_AUTH_WPA_VERSION_WPA:
sys_cfg.protocol = PROTOCOL_WPA;
break;
case IW_AUTH_WPA_VERSION_WPA2:
sys_cfg.protocol = PROTOCOL_WPA2;
break;
case IW_AUTH_WPA_VERSION_WPA | IW_AUTH_WPA_VERSION_WPA2:
sys_cfg.protocol = PROTOCOL_WPA2_MIXED;
break;
default:
break;
}
priv->uap_protocol = sys_cfg.protocol;
break;
case IW_AUTH_KEY_MGMT:
switch (vwrq->value) {
case IW_AUTH_KEY_MGMT_802_1X:
sys_cfg.key_mgmt |= KEY_MGMT_EAP;
priv->uap_key_mgmt |= KEY_MGMT_EAP;
break;
case IW_AUTH_KEY_MGMT_PSK:
sys_cfg.key_mgmt |= KEY_MGMT_PSK;
priv->uap_key_mgmt |= KEY_MGMT_PSK;
break;
default:
break;
}
break;
case IW_AUTH_WPA_ENABLED:
case IW_AUTH_TKIP_COUNTERMEASURES:
case IW_AUTH_DROP_UNENCRYPTED:
case IW_AUTH_RX_UNENCRYPTED_EAPOL:
case IW_AUTH_ROAMING_CONTROL:
case IW_AUTH_PRIVACY_INVOKED:
default:
LEAVE();
return -EOPNOTSUPP; /* No AP configuration */
}
if (!sys_cfg.key_mgmt)
sys_cfg.key_mgmt = priv->uap_key_mgmt;
if (sys_cfg.key_mgmt & KEY_MGMT_PSK)
sys_cfg.key_mgmt_operation |= 0x01;
if (sys_cfg.key_mgmt & KEY_MGMT_EAP)
sys_cfg.key_mgmt_operation |= 0x03;
if (!sys_cfg.protocol)
sys_cfg.protocol = priv->uap_protocol;
/* Set AP configuration */
if (MLAN_STATUS_SUCCESS != woal_set_get_sys_config(priv,
MLAN_ACT_SET,
MOAL_IOCTL_WAIT,
&sys_cfg)) {
PRINTM(MERROR, "Error setting AP confiruration\n");
ret = -EFAULT;
goto done;
}
done:
LEAVE();
return 0;
}
/**
* @brief Get authentication mode parameters
*
* @param dev A pointer to net_device structure
* @param info A pointer to iw_request_info structure
* @param vwrq A pointer to iw_param structure
* @param extra A pointer to extra data buf
*
* @return 0 --success, otherwise fail
*/
static int
woal_get_auth(struct net_device *dev, struct iw_request_info *info,
struct iw_param *vwrq, char *extra)
{
moal_private *priv = (moal_private *) netdev_priv(dev);
mlan_uap_bss_param ap_cfg;
ENTER();
if (MLAN_STATUS_SUCCESS != woal_set_get_sys_config(priv,
MLAN_ACT_GET,
MOAL_IOCTL_WAIT,
&ap_cfg)) {
PRINTM(MERROR, "Error getting AP confiruration\n");
LEAVE();
return -EFAULT;
}
switch (vwrq->flags & IW_AUTH_INDEX) {
case IW_AUTH_CIPHER_PAIRWISE:
if (ap_cfg.wpa_cfg.pairwise_cipher_wpa == CIPHER_TKIP ||
ap_cfg.wpa_cfg.pairwise_cipher_wpa2 == CIPHER_TKIP)
vwrq->value = IW_AUTH_CIPHER_TKIP;
else if (ap_cfg.wpa_cfg.pairwise_cipher_wpa == CIPHER_AES_CCMP
|| ap_cfg.wpa_cfg.pairwise_cipher_wpa2 ==
CIPHER_AES_CCMP)
vwrq->value = IW_AUTH_CIPHER_CCMP;
else
vwrq->value = IW_AUTH_CIPHER_NONE;
break;
case IW_AUTH_CIPHER_GROUP:
if (ap_cfg.wpa_cfg.group_cipher == CIPHER_TKIP)
vwrq->value = IW_AUTH_CIPHER_TKIP;
else if (ap_cfg.wpa_cfg.group_cipher == CIPHER_AES_CCMP)
vwrq->value = IW_AUTH_CIPHER_CCMP;
else
vwrq->value = IW_AUTH_CIPHER_NONE;
break;
case IW_AUTH_80211_AUTH_ALG:
if (ap_cfg.auth_mode == MLAN_AUTH_MODE_SHARED)
vwrq->value = IW_AUTH_ALG_SHARED_KEY;
else if (ap_cfg.auth_mode == MLAN_AUTH_MODE_NETWORKEAP)
vwrq->value = IW_AUTH_ALG_LEAP;
else
vwrq->value = IW_AUTH_ALG_OPEN_SYSTEM;
break;
case IW_AUTH_WPA_ENABLED:
if (ap_cfg.protocol == PROTOCOL_WPA ||
ap_cfg.protocol == PROTOCOL_WPA2 ||
ap_cfg.protocol == PROTOCOL_WPA2_MIXED)
vwrq->value = 1;
else
vwrq->value = 0;
break;
case IW_AUTH_KEY_MGMT:
if (ap_cfg.key_mgmt & KEY_MGMT_EAP)
vwrq->value |= IW_AUTH_KEY_MGMT_802_1X;
if (ap_cfg.key_mgmt & KEY_MGMT_PSK)
vwrq->value |= IW_AUTH_KEY_MGMT_PSK;
break;
case IW_AUTH_WPA_VERSION:
case IW_AUTH_TKIP_COUNTERMEASURES:
case IW_AUTH_DROP_UNENCRYPTED:
case IW_AUTH_RX_UNENCRYPTED_EAPOL:
case IW_AUTH_ROAMING_CONTROL:
case IW_AUTH_PRIVACY_INVOKED:
default:
LEAVE();
return -EOPNOTSUPP;
}
LEAVE();
return 0;
}
#endif /* WE >= 18 */
/* Data rate listing
* MULTI_BANDS:
* abg a b b/g
* Infra G(12) A(8) B(4) G(12)
* Adhoc A+B(12) A(8) B(4) B(4)
* non-MULTI_BANDS:
b b/g
* Infra B(4) G(12)
* Adhoc B(4) B(4)
*/
/**
* @brief Get Range Info
*
* @param dev A pointer to net_device structure
* @param info A pointer to iw_request_info structure
* @param dwrq A pointer to iw_point structure
* @param extra A pointer to extra data buf
*
* @return 0 --success, otherwise fail
*/
static int
woal_get_range(struct net_device *dev, struct iw_request_info *info,
struct iw_point *dwrq, char *extra)
{
moal_private *priv = (moal_private *) netdev_priv(dev);
mlan_uap_bss_param ap_cfg;
struct iw_range *range = (struct iw_range *)extra;
t_u8 band = 0;
int i;
ENTER();
if (MLAN_STATUS_SUCCESS != woal_set_get_sys_config(priv,
MLAN_ACT_GET,
MOAL_IOCTL_WAIT,
&ap_cfg)) {
PRINTM(MERROR, "Error getting AP confiruration\n");
LEAVE();
return -EFAULT;
}
dwrq->length = sizeof(struct iw_range);
memset(range, 0, sizeof(struct iw_range));
range->min_nwid = 0;
range->max_nwid = 0;
range->num_bitrates = MAX_DATA_RATES;
for (i = 0; i < MIN(MAX_DATA_RATES, IW_MAX_BITRATES) &&
ap_cfg.rates[i]; i++) {
range->bitrate[i] = (ap_cfg.rates[i] & 0x7f) * 500000;
}
range->num_bitrates = i;
PRINTM(MINFO, "IW_MAX_BITRATES=%d num_bitrates=%d\n",
IW_MAX_BITRATES, range->num_bitrates);
range->num_frequency = MIN(ap_cfg.num_of_chan, IW_MAX_FREQUENCIES);
for (i = 0; i < range->num_frequency; i++) {
range->freq[i].i = (long)ap_cfg.chan_list[i].chan_number;
band = ap_cfg.chan_list[i].band_config_type & BAND_CONFIG_5GHZ;
range->freq[i].m =
(long)channel_to_frequency(ap_cfg.chan_list[i].
chan_number, band) * 100000;
range->freq[i].e = 1;
}
PRINTM(MINFO, "IW_MAX_FREQUENCIES=%d num_frequency=%d\n",
IW_MAX_FREQUENCIES, range->num_frequency);
range->num_channels = range->num_frequency;
woal_sort_channels(&range->freq[0], range->num_frequency);
/*
* Set an indication of the max TCP throughput in bit/s that we can
* expect using this interface
*/
if (i > 2)
range->throughput = 5000 * 1000;
else
range->throughput = 1500 * 1000;
range->min_rts = MLAN_RTS_MIN_VALUE;
range->max_rts = MLAN_RTS_MAX_VALUE;
range->min_frag = MLAN_FRAG_MIN_VALUE;
range->max_frag = MLAN_FRAG_MAX_VALUE;
range->encoding_size[0] = 5;
range->encoding_size[1] = 13;
range->num_encoding_sizes = 2;
range->max_encoding_tokens = 4;
/** Minimum power period */
#define IW_POWER_PERIOD_MIN 1000000 /* 1 sec */
/** Maximum power period */
#define IW_POWER_PERIOD_MAX 120000000 /* 2 min */
/** Minimum power timeout value */
#define IW_POWER_TIMEOUT_MIN 1000 /* 1 ms */
/** Maximim power timeout value */
#define IW_POWER_TIMEOUT_MAX 1000000 /* 1 sec */
/* Power Management duration & timeout */
range->min_pmp = IW_POWER_PERIOD_MIN;
range->max_pmp = IW_POWER_PERIOD_MAX;
range->min_pmt = IW_POWER_TIMEOUT_MIN;
range->max_pmt = IW_POWER_TIMEOUT_MAX;
range->pmp_flags = IW_POWER_PERIOD;
range->pmt_flags = IW_POWER_TIMEOUT;
range->pm_capa = IW_POWER_PERIOD | IW_POWER_TIMEOUT | IW_POWER_ALL_R;
/*
* Minimum version we recommend
*/
range->we_version_source = 15;
/*
* Version we are compiled with
*/
range->we_version_compiled = WIRELESS_EXT;
range->retry_capa = IW_RETRY_LIMIT;
range->retry_flags = IW_RETRY_LIMIT | IW_RETRY_MAX;
range->min_retry = MLAN_TX_RETRY_MIN;
range->max_retry = MLAN_TX_RETRY_MAX;
#if (WIRELESS_EXT >= 18)
if (ap_cfg.protocol & PROTOCOL_WPA)
range->enc_capa |= IW_ENC_CAPA_WPA;
if (ap_cfg.protocol & PROTOCOL_WPA2)
range->enc_capa |= IW_ENC_CAPA_WPA2;
if (ap_cfg.wpa_cfg.pairwise_cipher_wpa == CIPHER_TKIP ||
ap_cfg.wpa_cfg.pairwise_cipher_wpa2 == CIPHER_TKIP ||
ap_cfg.wpa_cfg.group_cipher == CIPHER_TKIP)
range->enc_capa |= IW_ENC_CAPA_CIPHER_TKIP;
if (ap_cfg.wpa_cfg.pairwise_cipher_wpa == CIPHER_AES_CCMP ||
ap_cfg.wpa_cfg.pairwise_cipher_wpa2 == CIPHER_AES_CCMP ||
ap_cfg.wpa_cfg.group_cipher == CIPHER_AES_CCMP)
range->enc_capa |= IW_ENC_CAPA_CIPHER_CCMP;
#endif
LEAVE();
return 0;
}
/**
* @brief Set priv command
*
* @param dev A pointer to net_device structure
* @param info A pointer to iw_request_info structure
* @param dwrq A pointer to iw_point structure
* @param extra A pointer to extra data buf
*
* @return 0 --success, otherwise fail
*/
static int
woal_set_priv(struct net_device *dev, struct iw_request_info *info,
struct iw_point *dwrq, char *extra)
{
ENTER();
LEAVE();
return -EOPNOTSUPP;
}
/**
* @brief Set essid
*
* @param dev A pointer to net_device structure
* @param info A pointer to iw_request_info structure
* @param dwrq A pointer to iw_point structure
* @param extra A pointer to extra data buf
*
* @return 0--success, otherwise fail
*/
static int
woal_set_essid(struct net_device *dev, struct iw_request_info *info,
struct iw_point *dwrq, char *extra)
{
moal_private *priv = (moal_private *) netdev_priv(dev);
mlan_uap_bss_param sys_cfg;
int ret = 0;
ENTER();
/* Check the size of the string */
if (dwrq->length > IW_ESSID_MAX_SIZE + 1) {
ret = -E2BIG;
goto done;
}
/* Initialize the invalid values so that the correct values below are
downloaded to firmware */
woal_set_sys_config_invalid_data(&sys_cfg);
/* Set the SSID */
#if WIRELESS_EXT > 20
sys_cfg.ssid.ssid_len = dwrq->length;
#else
sys_cfg.ssid.ssid_len = dwrq->length - 1;
#endif
memcpy(sys_cfg.ssid.ssid, extra,
MIN(sys_cfg.ssid.ssid_len, MLAN_MAX_SSID_LENGTH));
if (!sys_cfg.ssid.ssid_len || sys_cfg.ssid.ssid[0] < 0x20) {
PRINTM(MERROR, "Invalid SSID - aborting set_essid\n");
ret = -EINVAL;
goto done;
}
PRINTM(MINFO, "Requested new SSID = %s\n",
(sys_cfg.ssid.ssid_len > 0) ?
(char *)sys_cfg.ssid.ssid : "NULL");
/* Set AP configuration */
if (MLAN_STATUS_SUCCESS != woal_set_get_sys_config(priv,
MLAN_ACT_SET,
MOAL_IOCTL_WAIT,
&sys_cfg)) {
PRINTM(MERROR, "Error setting AP confiruration\n");
ret = -EFAULT;
goto done;
}
done:
LEAVE();
return ret;
}
/**
* @brief Get current essid
*
* @param dev A pointer to net_device structure
* @param info A pointer to iw_request_info structure
* @param dwrq A pointer to iw_point structure
* @param extra A pointer to extra data buf
*
* @return 0--success, otherwise fail
*/
static int
woal_get_essid(struct net_device *dev, struct iw_request_info *info,
struct iw_point *dwrq, char *extra)
{
moal_private *priv = (moal_private *) netdev_priv(dev);
mlan_uap_bss_param ap_cfg;
ENTER();
if (MLAN_STATUS_SUCCESS != woal_set_get_sys_config(priv,
MLAN_ACT_GET,
MOAL_IOCTL_WAIT,
&ap_cfg)) {
PRINTM(MERROR, "Error getting AP confiruration\n");
LEAVE();
return -EFAULT;
}
if (priv->bss_started) {
dwrq->length = MIN(dwrq->length, ap_cfg.ssid.ssid_len);
memcpy(extra, ap_cfg.ssid.ssid, dwrq->length);
} else
dwrq->length = 0;
dwrq->flags = 1;
LEAVE();
return 0;
}
/**
* iwconfig settable callbacks
*/
static const iw_handler woal_handler[] = {
(iw_handler) woal_config_commit, /* SIOCSIWCOMMIT */
(iw_handler) woal_get_name, /* SIOCGIWNAME */
(iw_handler) NULL, /* SIOCSIWNWID */
(iw_handler) NULL, /* SIOCGIWNWID */
(iw_handler) woal_set_freq, /* SIOCSIWFREQ */
(iw_handler) woal_get_freq, /* SIOCGIWFREQ */
(iw_handler) woal_set_bss_mode, /* SIOCSIWMODE */
(iw_handler) woal_get_bss_mode, /* SIOCGIWMODE */
(iw_handler) NULL, /* SIOCSIWSENS */
(iw_handler) NULL, /* SIOCGIWSENS */
(iw_handler) NULL, /* SIOCSIWRANGE */
(iw_handler) woal_get_range, /* SIOCGIWRANGE */
(iw_handler) woal_set_priv, /* SIOCSIWPRIV */
(iw_handler) NULL, /* SIOCGIWPRIV */
(iw_handler) NULL, /* SIOCSIWSTATS */
(iw_handler) NULL, /* SIOCGIWSTATS */
#if WIRELESS_EXT > 15
#ifdef CONFIG_WEXT_SPY
iw_handler_set_spy, /* SIOCSIWSPY */
iw_handler_get_spy, /* SIOCGIWSPY */
iw_handler_set_thrspy, /* SIOCSIWTHRSPY */
iw_handler_get_thrspy, /* SIOCGIWTHRSPY */
#else
(iw_handler) NULL, /* -- hole -- */
(iw_handler) NULL, /* -- hole -- */
(iw_handler) NULL, /* -- hole -- */
(iw_handler) NULL, /* -- hole -- */
#endif
#else /* WIRELESS_EXT > 15 */
(iw_handler) NULL, /* -- hole -- */
(iw_handler) NULL, /* -- hole -- */
(iw_handler) NULL, /* -- hole -- */
(iw_handler) NULL, /* -- hole -- */
#endif /* WIRELESS_EXT > 15 */
(iw_handler) woal_set_wap, /* SIOCSIWAP */
(iw_handler) woal_get_wap, /* SIOCGIWAP */
#if WIRELESS_EXT >= 18
(iw_handler) woal_set_mlme, /* SIOCSIWMLME */
#else
(iw_handler) NULL, /* -- hole -- */
#endif
/* (iw_handler) wlan_get_aplist, *//* SIOCGIWAPLIST */
NULL, /* SIOCGIWAPLIST */
#if WIRELESS_EXT > 13
(iw_handler) NULL, /* SIOCSIWSCAN */
(iw_handler) NULL, /* SIOCGIWSCAN */
#else /* WIRELESS_EXT > 13 */
(iw_handler) NULL, /* SIOCSIWSCAN */
(iw_handler) NULL, /* SIOCGIWSCAN */
#endif /* WIRELESS_EXT > 13 */
(iw_handler) woal_set_essid, /* SIOCSIWESSID */
(iw_handler) woal_get_essid, /* SIOCGIWESSID */
(iw_handler) NULL, /* SIOCSIWNICKN */
(iw_handler) NULL, /* SIOCGIWNICKN */
(iw_handler) NULL, /* -- hole -- */
(iw_handler) NULL, /* -- hole -- */
(iw_handler) NULL, /* SIOCSIWRATE */
(iw_handler) NULL, /* SIOCGIWRATE */
(iw_handler) NULL, /* SIOCSIWRTS */
(iw_handler) NULL, /* SIOCGIWRTS */
(iw_handler) NULL, /* SIOCSIWFRAG */
(iw_handler) NULL, /* SIOCGIWFRAG */
(iw_handler) NULL, /* SIOCSIWTXPOW */
(iw_handler) NULL, /* SIOCGIWTXPOW */
(iw_handler) NULL, /* SIOCSIWRETRY */
(iw_handler) NULL, /* SIOCGIWRETRY */
(iw_handler) woal_set_encode, /* SIOCSIWENCODE */
(iw_handler) woal_get_encode, /* SIOCGIWENCODE */
(iw_handler) NULL, /* SIOCSIWPOWER */
(iw_handler) NULL, /* SIOCGIWPOWER */
#if (WIRELESS_EXT >= 18)
(iw_handler) NULL, /* -- hole -- */
(iw_handler) NULL, /* -- hole -- */
(iw_handler) woal_set_gen_ie, /* SIOCSIWGENIE */
(iw_handler) woal_get_gen_ie, /* SIOCGIWGENIE */
(iw_handler) woal_set_auth, /* SIOCSIWAUTH */
(iw_handler) woal_get_auth, /* SIOCGIWAUTH */
(iw_handler) woal_set_encode_ext, /* SIOCSIWENCODEEXT */
(iw_handler) woal_get_encode_ext, /* SIOCGIWENCODEEXT */
#endif /* WIRELESSS_EXT >= 18 */
};
/**
* iwpriv settable callbacks
*/
static const iw_handler woal_private_handler[] = {
NULL, /* SIOCIWFIRSTPRIV */
};
/********************************************************
Global Functions
********************************************************/
/** wlan_handler_def */
struct iw_handler_def woal_uap_handler_def = {
num_standard:ARRAY_SIZE(woal_handler),
num_private:ARRAY_SIZE(woal_private_handler),
num_private_args:ARRAY_SIZE(woal_uap_priv_args),
standard:(iw_handler *) woal_handler,
private:(iw_handler *) woal_private_handler,
private_args:(struct iw_priv_args *)woal_uap_priv_args,
#if WIRELESS_EXT > 20
get_wireless_stats:woal_get_uap_wireless_stats,
#endif
};
/**
* @brief Get wireless statistics
*
* @param dev A pointer to net_device structure
*
* @return A pointer to iw_statistics buf
*/
struct iw_statistics *
woal_get_uap_wireless_stats(struct net_device *dev)
{
moal_private *priv = (moal_private *) netdev_priv(dev);
t_u16 wait_option = MOAL_NO_WAIT;
ENTER();
/*
* Since schedule() is not allowed from an atomic context
* such as when dev_base_lock for netdevices is acquired
* for reading/writing in kernel before this call, HostCmd
* is issued in non-blocking way in such contexts and
* blocking in other cases.
*/
if (write_can_lock(&dev_base_lock)
&& (!in_atomic() || current->exit_state))
wait_option = MOAL_WSTATS_WAIT;
priv->w_stats.qual.qual = 0;
priv->w_stats.qual.level = 0;
priv->w_stats.discard.code = 0;
priv->w_stats.status = IW_MODE_MASTER;
woal_uap_get_stats(priv, wait_option, NULL);
LEAVE();
return &priv->w_stats;
}