diff options
Diffstat (limited to 'drivers/net/wireless/sd8797/mlinux/moal_wext.c')
-rw-r--r-- | drivers/net/wireless/sd8797/mlinux/moal_wext.c | 3051 |
1 files changed, 3051 insertions, 0 deletions
diff --git a/drivers/net/wireless/sd8797/mlinux/moal_wext.c b/drivers/net/wireless/sd8797/mlinux/moal_wext.c new file mode 100644 index 000000000000..a967c939f009 --- /dev/null +++ b/drivers/net/wireless/sd8797/mlinux/moal_wext.c @@ -0,0 +1,3051 @@ +/** @file moal_wext.c + * + * @brief This file contains wireless extension standard ioctl functions + * + * Copyright (C) 2008-2011, 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: + 10/21/2008: initial version +************************************************************************/ + +#include "moal_main.h" + +#ifdef STA_SUPPORT +/** Approximate amount of data needed to pass a scan result back to iwlist */ +#define MAX_SCAN_CELL_SIZE (IW_EV_ADDR_LEN \ + + MLAN_MAX_SSID_LENGTH \ + + IW_EV_UINT_LEN \ + + IW_EV_FREQ_LEN \ + + IW_EV_QUAL_LEN \ + + MLAN_MAX_SSID_LENGTH \ + + IW_EV_PARAM_LEN \ + + 40) /* 40 for WPAIE */ +/** Macro for minimum size of scan buffer */ +#define MIN_ACCEPTED_GET_SCAN_BUF 8000 + +/******************************************************** + Global Variables +********************************************************/ + +/******************************************************** + Local Functions +********************************************************/ +/** + * @brief This function validates a SSID as being able to be printed + * + * @param pssid SSID structure to validate + * + * @return MTRUE or MFALSE + */ +static BOOLEAN +woal_ssid_valid(mlan_802_11_ssid * pssid) +{ +#ifdef ASCII_SSID_CHECK + unsigned int ssid_idx; + + ENTER(); + + for (ssid_idx = 0; ssid_idx < pssid->ssid_len; ssid_idx++) { + if ((pssid->ssid[ssid_idx] < 0x20) || (pssid->ssid[ssid_idx] > 0x7e)) { + LEAVE(); + return MFALSE; + } + } + LEAVE(); +#endif + return MTRUE; +} + +/** + * @brief Compare two SSIDs + * + * @param ssid1 A pointer to ssid to compare + * @param ssid2 A pointer to ssid to compare + * + * @return 0--ssid is same, otherwise is different + */ +static t_s32 +woal_ssid_cmp(mlan_802_11_ssid * ssid1, mlan_802_11_ssid * ssid2) +{ + ENTER(); + + if (!ssid1 || !ssid2) { + LEAVE(); + return -1; + } + if (ssid1->ssid_len != ssid2->ssid_len) { + LEAVE(); + return -1; + } + + LEAVE(); + return memcmp(ssid1->ssid, ssid2->ssid, ssid1->ssid_len); +} + +/** + * @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 Convert RSSI to quality + * + * @param rssi RSSI in dBm + * + * @return Quality of the link (0-5) + */ +static t_u8 +woal_rssi_to_quality(t_s16 rssi) +{ +/** Macro for RSSI range */ +#define MOAL_RSSI_NO_SIGNAL -90 +#define MOAL_RSSI_VERY_LOW -80 +#define MOAL_RSSI_LOW -70 +#define MOAL_RSSI_GOOD -60 +#define MOAL_RSSI_VERY_GOOD -50 +#define MOAL_RSSI_INVALID 0 + if (rssi <= MOAL_RSSI_NO_SIGNAL || rssi == MOAL_RSSI_INVALID) + return 0; + else if (rssi <= MOAL_RSSI_VERY_LOW) + return 1; + else if (rssi <= MOAL_RSSI_LOW) + return 2; + else if (rssi <= MOAL_RSSI_GOOD) + return 3; + else if (rssi <= MOAL_RSSI_VERY_GOOD) + return 4; + else + return 5; +} + +/** + * @brief Set Adapter Node Name + * + * @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_nick(struct net_device *dev, struct iw_request_info *info, + struct iw_point *dwrq, char *extra) +{ + moal_private *priv = (moal_private *) netdev_priv(dev); + ENTER(); + /* + * Check the size of the string + */ + if (dwrq->length > 16) { + LEAVE(); + return -E2BIG; + } + memset(priv->nick_name, 0, sizeof(priv->nick_name)); + memcpy(priv->nick_name, extra, dwrq->length); + LEAVE(); + return 0; +} + +/** + * @brief Get Adapter Node Name + * + * @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 + */ +static int +woal_get_nick(struct net_device *dev, struct iw_request_info *info, + struct iw_point *dwrq, char *extra) +{ + moal_private *priv = (moal_private *) netdev_priv(dev); + ENTER(); + /* + * Get the Nick Name saved + */ + strncpy(extra, (char *) priv->nick_name, 16); + extra[16] = '\0'; + /* + * If none, we may want to get the one that was set + */ + + /* + * Push it out ! + */ + dwrq->length = strlen(extra) + 1; + 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 Set frequency + * + * @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) +{ + int ret = 0; + moal_private *priv = (moal_private *) netdev_priv(dev); + mlan_ds_bss *bss = NULL; + mlan_ioctl_req *req = NULL; + + ENTER(); + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + bss = (mlan_ds_bss *) req->pbuf; + /* + * If setting by frequency, convert to a channel + */ + if (fwrq->e == 1) { + long f = fwrq->m / 100000; + bss->param.bss_chan.freq = f; + } else + bss->param.bss_chan.channel = fwrq->m; + + bss->sub_command = MLAN_OID_BSS_CHANNEL; + req->req_id = MLAN_IOCTL_BSS; + req->action = MLAN_ACT_SET; + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto done; + } + if (MLAN_STATUS_SUCCESS != + woal_change_adhoc_chan(priv, bss->param.bss_chan.channel)) + ret = -EFAULT; + + done: + if (req) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Get frequency + * + * @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) +{ + int ret = 0; + moal_private *priv = (moal_private *) netdev_priv(dev); + mlan_ds_bss *bss = NULL; + mlan_ioctl_req *req = NULL; + + ENTER(); + + 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_BSS_CHANNEL; + req->req_id = MLAN_IOCTL_BSS; + req->action = MLAN_ACT_GET; + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto done; + } + fwrq->m = (long) bss->param.bss_chan.freq * 100000; + fwrq->i = (long) bss->param.bss_chan.channel; + fwrq->e = 1; + fwrq->flags = IW_FREQ_FIXED; + done: + if (req) + kfree(req); + + 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 Wireless mode to set + * @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; + moal_private *priv = (moal_private *) netdev_priv(dev); + mlan_ds_bss *bss = NULL; + mlan_ioctl_req *req = NULL; + + ENTER(); + + 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_BSS_MODE; + req->req_id = MLAN_IOCTL_BSS; + req->action = MLAN_ACT_SET; + + switch (*uwrq) { + case IW_MODE_INFRA: + bss->param.bss_mode = MLAN_BSS_MODE_INFRA; + break; + case IW_MODE_ADHOC: + bss->param.bss_mode = MLAN_BSS_MODE_IBSS; + break; + case IW_MODE_AUTO: + bss->param.bss_mode = MLAN_BSS_MODE_AUTO; + break; + default: + ret = -EINVAL; + break; + } + if (ret) + goto done; + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto done; + } + done: + if (req) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @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) +{ + int ret = 0; + moal_private *priv = (moal_private *) netdev_priv(dev); + mlan_bss_info bss_info; + + ENTER(); + + memset(&bss_info, 0, sizeof(bss_info)); + + woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info); + + if (bss_info.media_connected == MTRUE) { + memcpy(awrq->sa_data, &bss_info.bssid, MLAN_MAC_ADDR_LENGTH); + } else { + memset(awrq->sa_data, 0, MLAN_MAC_ADDR_LENGTH); + } + awrq->sa_family = ARPHRD_ETHER; + + LEAVE(); + return ret; +} + +/** + * @brief Connect to the AP or Ad-hoc Network with specific bssid + * + * NOTE: Scan should be issued by application before this function is called + * + * @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; + const t_u8 bcast[MLAN_MAC_ADDR_LENGTH] = { 255, 255, 255, 255, 255, 255 }; + const t_u8 zero_mac[MLAN_MAC_ADDR_LENGTH] = { 0, 0, 0, 0, 0, 0 }; + moal_private *priv = (moal_private *) netdev_priv(dev); + mlan_ssid_bssid ssid_bssid; + mlan_bss_info bss_info; + + ENTER(); + + if (awrq->sa_family != ARPHRD_ETHER) { + ret = -EINVAL; + goto done; + } + + PRINTM(MINFO, "ASSOC: WAP: sa_data: %02x:%02x:%02x:%02x:%02x:%02x\n", + (t_u8) awrq->sa_data[0], (t_u8) awrq->sa_data[1], + (t_u8) awrq->sa_data[2], (t_u8) awrq->sa_data[3], + (t_u8) awrq->sa_data[4], (t_u8) awrq->sa_data[5]); + + if (MLAN_STATUS_SUCCESS != + woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info)) { + ret = -EFAULT; + goto done; + } +#ifdef REASSOCIATION + /* Cancel re-association */ + priv->reassoc_required = MFALSE; +#endif + + /* zero_mac means disconnect */ + if (!memcmp(zero_mac, awrq->sa_data, MLAN_MAC_ADDR_LENGTH)) { + woal_disconnect(priv, MOAL_IOCTL_WAIT, NULL); + goto done; + } + + /* Broadcast MAC means search for best network */ + memset(&ssid_bssid, 0, sizeof(mlan_ssid_bssid)); + + if (memcmp(bcast, awrq->sa_data, MLAN_MAC_ADDR_LENGTH)) { + /* Check if we are already assoicated to the AP */ + if (bss_info.media_connected == MTRUE) { + if (!memcmp(awrq->sa_data, &bss_info.bssid, ETH_ALEN)) + goto done; + /* disconnect before try to assoicate to the new AP */ + woal_disconnect(priv, MOAL_IOCTL_WAIT, NULL); + } + memcpy(&ssid_bssid.bssid, awrq->sa_data, ETH_ALEN); + } + + if (MLAN_STATUS_SUCCESS != woal_find_best_network(priv, + MOAL_IOCTL_WAIT, + &ssid_bssid)) { + PRINTM(MERROR, "ASSOC: WAP: MAC address not found in BSSID List\n"); + ret = -ENETUNREACH; + goto done; + } + /* Zero SSID implies use BSSID to connect */ + memset(&ssid_bssid.ssid, 0, sizeof(mlan_802_11_ssid)); + if (MLAN_STATUS_SUCCESS != woal_bss_start(priv, + MOAL_IOCTL_WAIT, &ssid_bssid)) { + ret = -EFAULT; + goto done; + } +#ifdef REASSOCIATION + memset(&bss_info, 0, sizeof(bss_info)); + if (MLAN_STATUS_SUCCESS != woal_get_bss_info(priv, + MOAL_IOCTL_WAIT, &bss_info)) { + ret = -EFAULT; + goto done; + } + memcpy(&priv->prev_ssid_bssid.ssid, &bss_info.ssid, + sizeof(mlan_802_11_ssid)); + memcpy(&priv->prev_ssid_bssid.bssid, &bss_info.bssid, MLAN_MAC_ADDR_LENGTH); +#endif /* REASSOCIATION */ + + done: + + 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) +{ + moal_private *priv = (moal_private *) netdev_priv(dev); + ENTER(); + *uwrq = woal_get_mode(priv, MOAL_IOCTL_WAIT); + LEAVE(); + return 0; +} + +/** + * @brief Set sensitivity + * + * @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 + */ +static int +woal_set_sens(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + int ret = 0; + + ENTER(); + + LEAVE(); + return ret; +} + +/** + * @brief Get sensitivity + * + * @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 -1 + */ +static int +woal_get_sens(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + int ret = -1; + + ENTER(); + + LEAVE(); + return ret; +} + +/** + * @brief Set Tx power + * + * @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_txpow(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_power_cfg_t power_cfg; + + ENTER(); + if (vwrq->disabled) { + woal_set_radio(priv, 0); + goto done; + } + woal_set_radio(priv, 1); + + if (!vwrq->fixed) + power_cfg.is_power_auto = 1; + else { + power_cfg.is_power_auto = 0; + power_cfg.power_level = vwrq->value; + } + + if (MLAN_STATUS_SUCCESS != + woal_set_get_tx_power(priv, MLAN_ACT_SET, &power_cfg)) { + ret = -EFAULT; + goto done; + } + + done: + LEAVE(); + return ret; +} + +/** + * @brief Get Tx power + * + * @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_txpow(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_power_cfg_t power_cfg; + mlan_bss_info bss_info; + + ENTER(); + + memset(&power_cfg, 0, sizeof(mlan_power_cfg_t)); + memset(&bss_info, 0, sizeof(bss_info)); + woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info); + + if (MLAN_STATUS_SUCCESS != + woal_set_get_tx_power(priv, MLAN_ACT_GET, &power_cfg)) { + ret = -EFAULT; + goto done; + } + + vwrq->value = power_cfg.power_level; + if (power_cfg.is_power_auto) + vwrq->fixed = 0; + else + vwrq->fixed = 1; + if (bss_info.radio_on) { + vwrq->disabled = 0; + vwrq->flags = IW_TXPOW_DBM; + } else { + vwrq->disabled = 1; + } + + done: + LEAVE(); + return ret; +} + +/** + * @brief Set power management + * + * @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 MLAN_STATUS_SUCCESS --success, otherwise fail + */ +static int +woal_set_power(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + int ret = 0, disabled; + moal_private *priv = (moal_private *) netdev_priv(dev); + + ENTER(); + + disabled = vwrq->disabled; + + if (MLAN_STATUS_SUCCESS != woal_set_get_power_mgmt(priv, + MLAN_ACT_SET, &disabled, + vwrq->flags)) { + ret = -EFAULT; + } + + LEAVE(); + return ret; +} + +/** + * @brief Get power management + * + * @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 MLAN_STATUS_SUCCESS --success, otherwise fail + */ +static int +woal_get_power(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + int ret = 0, ps_mode; + moal_private *priv = (moal_private *) netdev_priv(dev); + + ENTER(); + + if (MLAN_STATUS_SUCCESS != woal_set_get_power_mgmt(priv, + MLAN_ACT_GET, &ps_mode, + 0)) { + ret = -EFAULT; + } + + if (ps_mode) + vwrq->disabled = 0; + else + vwrq->disabled = 1; + + vwrq->value = 0; + + LEAVE(); + return ret; +} + +/** + * @brief Set Tx retry count + * + * @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_retry(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + int ret = 0, retry_val = vwrq->value; + moal_private *priv = (moal_private *) netdev_priv(dev); + + ENTER(); + + if (vwrq->flags == IW_RETRY_LIMIT) { + /* + * The MAC has a 4-bit Total_Tx_Count register + * Total_Tx_Count = 1 + Tx_Retry_Count + */ + + if (MLAN_STATUS_SUCCESS != + woal_set_get_retry(priv, MLAN_ACT_SET, + MOAL_IOCTL_WAIT, &retry_val)) { + ret = -EFAULT; + goto done; + } + } else { + ret = -EOPNOTSUPP; + goto done; + } + + done: + LEAVE(); + return ret; +} + +/** + * @brief Get Tx retry count + * + * @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_retry(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + int retry_val, ret = 0; + moal_private *priv = (moal_private *) netdev_priv(dev); + + ENTER(); + + if (MLAN_STATUS_SUCCESS != + woal_set_get_retry(priv, MLAN_ACT_GET, MOAL_IOCTL_WAIT, &retry_val)) { + ret = -EFAULT; + goto done; + } + + vwrq->disabled = 0; + if (!vwrq->flags) { + vwrq->flags = IW_RETRY_LIMIT; + /* Get Tx retry count */ + vwrq->value = retry_val; + } + + done: + LEAVE(); + return ret; +} + +/** + * @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_ds_sec_cfg *sec = NULL; + mlan_ioctl_req *req = NULL; + int index = 0; + t_u32 auth_mode = 0; + + ENTER(); + + 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; + + /* Check index */ + index = (dwrq->flags & IW_ENCODE_INDEX) - 1; + if (index > 3) { + PRINTM(MERROR, "Key index #%d out of range\n", index); + ret = -EINVAL; + goto done; + } + + sec->param.encrypt_key.key_len = 0; + 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 = -EINVAL; + goto done; + } + if (index < 0) + sec->param.encrypt_key.key_index = MLAN_KEY_INDEX_DEFAULT; + else + sec->param.encrypt_key.key_index = index; + memcpy(sec->param.encrypt_key.key_material, extra, dwrq->length); + /* Set the length */ + if (dwrq->length > MIN_WEP_KEY_SIZE) + sec->param.encrypt_key.key_len = MAX_WEP_KEY_SIZE; + else + sec->param.encrypt_key.key_len = MIN_WEP_KEY_SIZE; + } 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"); + sec->param.encrypt_key.key_disable = MTRUE; + } else { + /* + * iwconfig mlanX key [n] + * iwconfig mlanX key on + * iwconfig mlanX key open + * iwconfig mlanX key restricted + * Do we want to just set the transmit key index ? + */ + if (index < 0) { + PRINTM(MINFO, "*** iwconfig mlanX key on ***\n"); + sec->param.encrypt_key.key_index = MLAN_KEY_INDEX_DEFAULT; + } else + sec->param.encrypt_key.key_index = index; + sec->param.encrypt_key.is_current_wep_key = MTRUE; + } + } + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto done; + } + if (dwrq->flags & (IW_ENCODE_RESTRICTED | IW_ENCODE_OPEN)) { + switch (dwrq->flags & 0xf000) { + case IW_ENCODE_RESTRICTED: + /* iwconfig mlanX restricted key [1] */ + auth_mode = MLAN_AUTH_MODE_SHARED; + PRINTM(MINFO, "Auth mode restricted!\n"); + break; + case IW_ENCODE_OPEN: + /* iwconfig mlanX key [2] open */ + 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 */ + auth_mode = MLAN_AUTH_MODE_AUTO; + PRINTM(MINFO, "Auth mode auto!\n"); + break; + } + if (MLAN_STATUS_SUCCESS != + woal_set_auth_mode(priv, MOAL_IOCTL_WAIT, auth_mode)) + ret = -EFAULT; + } + done: + if (req) + kfree(req); + + 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) +{ + int ret = 0; + moal_private *priv = (moal_private *) netdev_priv(dev); + mlan_ds_sec_cfg *sec = NULL; + mlan_ioctl_req *req = NULL; + t_u32 auth_mode; + int index = (dwrq->flags & IW_ENCODE_INDEX); + + 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_get_auth_mode(priv, MOAL_IOCTL_WAIT, &auth_mode)) { + ret = -EFAULT; + goto done; + } + dwrq->flags = 0; + /* + * Check encryption mode + */ + switch (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; + + case MLAN_AUTH_MODE_AUTO: + dwrq->flags = IW_ENCODE_OPEN | IW_ENCODE_RESTRICTED; + break; + + default: + dwrq->flags = IW_ENCODE_DISABLED | IW_ENCODE_OPEN; + break; + } + 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_GET; + + if (!index) + sec->param.encrypt_key.key_index = MLAN_KEY_INDEX_DEFAULT; + else + sec->param.encrypt_key.key_index = index - 1; + + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto done; + } + memset(extra, 0, 16); + if (sec->param.encrypt_key.key_len) { + memcpy(extra, sec->param.encrypt_key.key_material, + sec->param.encrypt_key.key_len); + dwrq->length = sec->param.encrypt_key.key_len; + dwrq->flags |= (sec->param.encrypt_key.key_index + 1); + dwrq->flags &= ~IW_ENCODE_DISABLED; + } else if (sec->param.encrypt_key.key_disable) + dwrq->flags |= IW_ENCODE_DISABLED; + else + dwrq->flags &= ~IW_ENCODE_DISABLED; + + dwrq->flags |= IW_ENCODE_NOKEY; + done: + if (req) + kfree(req); + + LEAVE(); + return ret; +} + +/** + * @brief Set data rate + * + * @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_rate(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_rate_cfg_t rate_cfg; + + ENTER(); + + if (vwrq->value == -1) { + rate_cfg.is_rate_auto = 1; + } else { + rate_cfg.is_rate_auto = 0; + rate_cfg.rate_type = MLAN_RATE_VALUE; + rate_cfg.rate = vwrq->value / 500000; + } + if (MLAN_STATUS_SUCCESS != woal_set_get_data_rate(priv, + MLAN_ACT_SET, + &rate_cfg)) { + ret = -EFAULT; + } + + LEAVE(); + return ret; +} + +/** + * @brief Get data rate + * + * @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_rate(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_rate_cfg_t rate_cfg; + + ENTER(); + + if (MLAN_STATUS_SUCCESS != woal_set_get_data_rate(priv, + MLAN_ACT_GET, + &rate_cfg)) { + ret = -EFAULT; + goto done; + } + + if (rate_cfg.is_rate_auto) + vwrq->fixed = 0; + else + vwrq->fixed = 1; + vwrq->value = rate_cfg.rate * 500000; + done: + LEAVE(); + return ret; +} + +/** + * @brief Set RTS threshold + * + * @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_rts(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); + int rthr = vwrq->value; + + ENTER(); + + if (vwrq->disabled) { + rthr = MLAN_RTS_MAX_VALUE; + } else { + if (rthr < MLAN_RTS_MIN_VALUE || rthr > MLAN_RTS_MAX_VALUE) { + ret = -EINVAL; + goto done; + } + } + + if (MLAN_STATUS_SUCCESS != + woal_set_get_rts(priv, MLAN_ACT_SET, MOAL_IOCTL_WAIT, &rthr)) { + ret = -EFAULT; + goto done; + } + + done: + LEAVE(); + return ret; +} + +/** + * @brief Get RTS threshold + * + * @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_rts(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + int rthr, ret = 0; + moal_private *priv = (moal_private *) netdev_priv(dev); + + ENTER(); + + if (MLAN_STATUS_SUCCESS != + woal_set_get_rts(priv, MLAN_ACT_GET, MOAL_IOCTL_WAIT, &rthr)) { + ret = -EFAULT; + goto done; + } + + vwrq->value = rthr; + vwrq->disabled = ((vwrq->value < MLAN_RTS_MIN_VALUE) + || (vwrq->value > MLAN_RTS_MAX_VALUE)); + vwrq->fixed = 1; + + done: + LEAVE(); + return ret; +} + +/** + * @brief Set Fragment threshold + * + * @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_frag(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); + int fthr = vwrq->value; + + ENTER(); + + if (vwrq->disabled) { + fthr = MLAN_FRAG_MAX_VALUE; + } else { + if (fthr < MLAN_FRAG_MIN_VALUE || fthr > MLAN_FRAG_MAX_VALUE) { + ret = -EINVAL; + goto done; + } + } + + if (MLAN_STATUS_SUCCESS != + woal_set_get_frag(priv, MLAN_ACT_SET, MOAL_IOCTL_WAIT, &fthr)) { + ret = -EFAULT; + goto done; + } + + done: + LEAVE(); + return ret; +} + +/** + * @brief Get Fragment threshold + * + * @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_frag(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + int ret = 0, fthr; + moal_private *priv = (moal_private *) netdev_priv(dev); + + ENTER(); + + if (MLAN_STATUS_SUCCESS != + woal_set_get_frag(priv, MLAN_ACT_GET, MOAL_IOCTL_WAIT, &fthr)) { + ret = -EFAULT; + goto done; + } + + vwrq->value = fthr; + vwrq->disabled = ((vwrq->value < MLAN_FRAG_MIN_VALUE) + || (vwrq->value > MLAN_FRAG_MAX_VALUE)); + vwrq->fixed = 1; + + 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 0 --success, otherwise fail + */ +static int +woal_get_gen_ie(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); + int copy_size = 0, ie_len; + t_u8 ie[MAX_IE_SIZE]; + + ENTER(); + + if (MLAN_STATUS_SUCCESS != + woal_set_get_gen_ie(priv, MLAN_ACT_GET, ie, &ie_len)) { + ret = -EFAULT; + goto done; + } + + copy_size = MIN(ie_len, dwrq->length); + memcpy(extra, ie, copy_size); + dwrq->length = copy_size; + + done: + LEAVE(); + return ret; +} + +/** + * @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) +{ + int ret = 0; + moal_private *priv = (moal_private *) netdev_priv(dev); + int ie_len = dwrq->length; + const t_u8 wps_oui[] = { 0x00, 0x50, 0xf2, 0x04 }; + mlan_ds_wps_cfg *pwps = NULL; + mlan_ioctl_req *req = NULL; + + ENTER(); + + /* extra + 2 to skip element id and length */ + if (!memcmp((t_u8 *) (extra + 2), wps_oui, sizeof(wps_oui))) { + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_wps_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + pwps = (mlan_ds_wps_cfg *) req->pbuf; + req->req_id = MLAN_IOCTL_WPS_CFG; + req->action = MLAN_ACT_SET; + pwps->sub_command = MLAN_OID_WPS_CFG_SESSION; + pwps->param.wps_session = MLAN_WPS_CFG_SESSION_START; + + if (MLAN_STATUS_SUCCESS != + woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto done; + } + } + + if (MLAN_STATUS_SUCCESS != + woal_set_get_gen_ie(priv, MLAN_ACT_SET, (t_u8 *) extra, &ie_len)) { + ret = -EFAULT; + goto done; + } + + done: + if (req) + 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 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; + int ret = 0; + + ENTER(); + key_index = (dwrq->flags & IW_ENCODE_INDEX) - 1; + if (key_index < 0 || key_index > 3) { + ret = -EINVAL; + goto done; + } + if (ext->key_len > (dwrq->length - sizeof(struct iw_encode_ext))) { + ret = -EINVAL; + goto done; + } + 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; + pkey_material = (t_u8 *) (ext + 1); + sec->param.encrypt_key.key_len = ext->key_len; + memcpy(sec->param.encrypt_key.mac_addr, (u8 *) ext->addr.sa_data, ETH_ALEN); + /* Disable and Remove Key */ + if ((dwrq->flags & IW_ENCODE_DISABLED) && !ext->key_len) { + sec->param.encrypt_key.key_remove = MTRUE; + sec->param.encrypt_key.key_index = key_index; + sec->param.encrypt_key.key_flags = KEY_FLAG_REMOVE_KEY; + PRINTM(MIOCTL, + "Remove key key_index=%d, dwrq->flags=0x%x %02x:%02x:%02x:%02x:%02x:%02x\n", + key_index, dwrq->flags, sec->param.encrypt_key.mac_addr[0], + sec->param.encrypt_key.mac_addr[1], + sec->param.encrypt_key.mac_addr[2], + sec->param.encrypt_key.mac_addr[3], + sec->param.encrypt_key.mac_addr[4], + sec->param.encrypt_key.mac_addr[5]); + } else if (ext->key_len <= MAX_WEP_KEY_SIZE) { + /* Set WEP key */ + sec->param.encrypt_key.key_index = key_index; + sec->param.encrypt_key.key_flags = ext->ext_flags; + memcpy(sec->param.encrypt_key.key_material, pkey_material, + ext->key_len); + if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) + sec->param.encrypt_key.is_current_wep_key = MTRUE; + } else { + /* Set WPA key */ + sec->param.encrypt_key.key_index = key_index; + 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, "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, "Tx PN", sec->param.encrypt_key.pn, + SEQ_MAX_SIZE); + } + memcpy(sec->param.encrypt_key.key_material, pkey_material, + ext->key_len); + PRINTM(MIOCTL, + "set wpa key key_index=%d, key_len=%d key_flags=0x%x %02x:%02x:%02x:%02x:%02x:%02x\n", + key_index, ext->key_len, sec->param.encrypt_key.key_flags, + sec->param.encrypt_key.mac_addr[0], + sec->param.encrypt_key.mac_addr[1], + sec->param.encrypt_key.mac_addr[2], + sec->param.encrypt_key.mac_addr[3], + sec->param.encrypt_key.mac_addr[4], + sec->param.encrypt_key.mac_addr[5]); + DBG_HEXDUMP(MCMD_D, "wpa key", pkey_material, ext->key_len); +#define IW_ENCODE_ALG_SMS4 0x20 + /* Set WAPI key */ + if (ext->alg == IW_ENCODE_ALG_SMS4) { + sec->param.encrypt_key.is_wapi_key = MTRUE; + memcpy(sec->param.encrypt_key.pn, (t_u8 *) ext->tx_seq, + SEQ_MAX_SIZE); + memcpy(&sec->param.encrypt_key.pn[SEQ_MAX_SIZE], + (t_u8 *) ext->rx_seq, SEQ_MAX_SIZE); + DBG_HEXDUMP(MCMD_D, "WAPI PN", sec->param.encrypt_key.pn, PN_SIZE); + } + } + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) + ret = -EFAULT; + done: + if (req) + 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); + int ret = 0; + + ENTER(); + if ((mlme->cmd == IW_MLME_DEAUTH) || (mlme->cmd == IW_MLME_DISASSOC)) { + + if (MLAN_STATUS_SUCCESS != + woal_disconnect(priv, MOAL_IOCTL_WAIT, (t_u8 *) mlme->addr.sa_data)) + ret = -EFAULT; + } + 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); + t_u32 auth_mode = 0; + t_u32 encrypt_mode = 0; + ENTER(); + + switch (vwrq->flags & IW_AUTH_INDEX) { + case IW_AUTH_CIPHER_PAIRWISE: + case IW_AUTH_CIPHER_GROUP: + if (vwrq->value & IW_AUTH_CIPHER_NONE) + encrypt_mode = MLAN_ENCRYPTION_MODE_NONE; + else if (vwrq->value & IW_AUTH_CIPHER_WEP40) + encrypt_mode = MLAN_ENCRYPTION_MODE_WEP40; + else if (vwrq->value & IW_AUTH_CIPHER_WEP104) + encrypt_mode = MLAN_ENCRYPTION_MODE_WEP104; + else if (vwrq->value & IW_AUTH_CIPHER_TKIP) + encrypt_mode = MLAN_ENCRYPTION_MODE_TKIP; + else if (vwrq->value & IW_AUTH_CIPHER_CCMP) + encrypt_mode = MLAN_ENCRYPTION_MODE_CCMP; + if (MLAN_STATUS_SUCCESS != + woal_set_encrypt_mode(priv, MOAL_IOCTL_WAIT, encrypt_mode)) + ret = -EFAULT; + break; + case IW_AUTH_80211_AUTH_ALG: + switch (vwrq->value) { + case IW_AUTH_ALG_SHARED_KEY: + PRINTM(MINFO, "Auth mode shared key!\n"); + auth_mode = MLAN_AUTH_MODE_SHARED; + break; + case IW_AUTH_ALG_LEAP: + PRINTM(MINFO, "Auth mode LEAP!\n"); + auth_mode = MLAN_AUTH_MODE_NETWORKEAP; + break; + case IW_AUTH_ALG_OPEN_SYSTEM: + PRINTM(MINFO, "Auth mode open!\n"); + auth_mode = MLAN_AUTH_MODE_OPEN; + break; + case IW_AUTH_ALG_SHARED_KEY | IW_AUTH_ALG_OPEN_SYSTEM: + default: + PRINTM(MINFO, "Auth mode auto!\n"); + auth_mode = MLAN_AUTH_MODE_AUTO; + break; + } + if (MLAN_STATUS_SUCCESS != + woal_set_auth_mode(priv, MOAL_IOCTL_WAIT, auth_mode)) + ret = -EFAULT; + break; + case IW_AUTH_WPA_ENABLED: + if (MLAN_STATUS_SUCCESS != + woal_set_wpa_enable(priv, MOAL_IOCTL_WAIT, vwrq->value)) + ret = -EFAULT; + break; +#define IW_AUTH_WAPI_ENABLED 0x20 + case IW_AUTH_WAPI_ENABLED: + if (MLAN_STATUS_SUCCESS != + woal_set_wapi_enable(priv, MOAL_IOCTL_WAIT, vwrq->value)) + ret = -EFAULT; + break; + case IW_AUTH_WPA_VERSION: + /* set WPA_VERSION_DISABLED/VERSION_WPA/VERSION_WP2 */ + priv->wpa_version = vwrq->value; + break; + case IW_AUTH_KEY_MGMT: + /* set KEY_MGMT_802_1X/KEY_MGMT_PSK */ + priv->key_mgmt = vwrq->value; + break; + 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: + break; + default: + ret = -EOPNOTSUPP; + break; + } + LEAVE(); + return ret; +} + +/** + * @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) +{ + int ret = 0; + moal_private *priv = (moal_private *) netdev_priv(dev); + t_u32 encrypt_mode = 0; + t_u32 auth_mode; + t_u32 wpa_enable; + ENTER(); + switch (vwrq->flags & IW_AUTH_INDEX) { + case IW_AUTH_CIPHER_PAIRWISE: + case IW_AUTH_CIPHER_GROUP: + if (MLAN_STATUS_SUCCESS != + woal_get_encrypt_mode(priv, MOAL_IOCTL_WAIT, &encrypt_mode)) + ret = -EFAULT; + else { + if (encrypt_mode == MLAN_ENCRYPTION_MODE_NONE) + vwrq->value = IW_AUTH_CIPHER_NONE; + else if (encrypt_mode == MLAN_ENCRYPTION_MODE_WEP40) + vwrq->value = IW_AUTH_CIPHER_WEP40; + else if (encrypt_mode == MLAN_ENCRYPTION_MODE_TKIP) + vwrq->value = IW_AUTH_CIPHER_TKIP; + else if (encrypt_mode == MLAN_ENCRYPTION_MODE_CCMP) + vwrq->value = IW_AUTH_CIPHER_CCMP; + else if (encrypt_mode == MLAN_ENCRYPTION_MODE_WEP104) + vwrq->value = IW_AUTH_CIPHER_WEP104; + } + break; + case IW_AUTH_80211_AUTH_ALG: + if (MLAN_STATUS_SUCCESS != + woal_get_auth_mode(priv, MOAL_IOCTL_WAIT, &auth_mode)) + ret = -EFAULT; + else { + if (auth_mode == MLAN_AUTH_MODE_SHARED) + vwrq->value = IW_AUTH_ALG_SHARED_KEY; + else if (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 (MLAN_STATUS_SUCCESS != + woal_get_wpa_enable(priv, MOAL_IOCTL_WAIT, &wpa_enable)) + ret = -EFAULT; + else + vwrq->value = wpa_enable; + break; + case IW_AUTH_WPA_VERSION: + vwrq->value = priv->wpa_version; + break; + case IW_AUTH_KEY_MGMT: + vwrq->value = priv->key_mgmt; + break; + 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: + ret = -EOPNOTSUPP; + goto done; + } + + done: + LEAVE(); + return ret; +} + +/** + * @brief Set PMKSA Cache + * + * @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 -EOPNOTSUPP + */ +static int +woal_set_pmksa(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + ENTER(); + LEAVE(); + return -EOPNOTSUPP; +} + +#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) +{ + int i; + moal_private *priv = (moal_private *) netdev_priv(dev); + struct iw_range *range = (struct iw_range *) extra; + moal_802_11_rates rates; + mlan_chan_list *pchan_list = NULL; + mlan_bss_info bss_info; + + ENTER(); + + if (!(pchan_list = kmalloc(sizeof(mlan_chan_list), GFP_KERNEL))) { + LEAVE(); + return -ENOMEM; + } + + dwrq->length = sizeof(struct iw_range); + memset(range, 0, sizeof(struct iw_range)); + + range->min_nwid = 0; + range->max_nwid = 0; + + memset(&rates, 0, sizeof(rates)); + woal_get_data_rates(priv, MOAL_IOCTL_WAIT, &rates); + range->num_bitrates = rates.num_of_rates; + + for (i = 0; i < MIN(range->num_bitrates, IW_MAX_BITRATES) && rates.rates[i]; + i++) { + range->bitrate[i] = (rates.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 = 0; + + memset(pchan_list, 0, sizeof(mlan_chan_list)); + + woal_get_channel_list(priv, MOAL_IOCTL_WAIT, pchan_list); + + range->num_frequency = MIN(pchan_list->num_of_chan, IW_MAX_FREQUENCIES); + + for (i = 0; i < range->num_frequency; i++) { + range->freq[i].i = (long) pchan_list->cf[i].channel; + range->freq[i].m = (long) pchan_list->cf[i].freq * 100000; + range->freq[i].e = 1; + } + kfree(pchan_list); + + 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; + + /* + * Set the qual, level and noise range values + */ + /* + * need to put the right values here + */ +/** Maximum quality percentage */ +#define IW_MAX_QUAL_PERCENT 5 +/** Average quality percentage */ +#define IW_AVG_QUAL_PERCENT 3 + range->max_qual.qual = IW_MAX_QUAL_PERCENT; + range->max_qual.level = 0; + range->max_qual.noise = 0; + + range->avg_qual.qual = IW_AVG_QUAL_PERCENT; + range->avg_qual.level = 0; + range->avg_qual.noise = 0; + + range->sensitivity = 0; + + /* + * Setup the supported power level ranges + */ + memset(range->txpower, 0, sizeof(range->txpower)); + + memset(&bss_info, 0, sizeof(bss_info)); + + woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info); + + range->txpower[0] = bss_info.min_power_level; + range->txpower[1] = bss_info.max_power_level; + range->num_txpower = 2; + range->txpower_capa = IW_TXPOW_DBM | IW_TXPOW_RANGE; + + LEAVE(); + return 0; +} + +#ifdef MEF_CFG_RX_FILTER +/** + * @brief Enable/disable Rx broadcast/multicast filter in non-HS mode + * + * @param priv A pointer to moal_private structure + * @param enable MTRUE/MFALSE: enable/disable + * + * @return 0 -- success, otherwise fail + */ +static int +woal_set_rxfilter(moal_private * priv, BOOLEAN enable) +{ + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *misc = NULL; + mlan_ds_misc_mef_cfg *mef_cfg = NULL; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + misc = (mlan_ds_misc_cfg *) req->pbuf; + mef_cfg = &misc->param.mef_cfg; + req->req_id = MLAN_IOCTL_MISC_CFG; + misc->sub_command = MLAN_OID_MISC_MEF_CFG; + req->action = MLAN_ACT_SET; + + mef_cfg->sub_id = (enable ? MEF_CFG_RX_FILTER_ENABLE : MEF_CFG_DISABLE); + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto done; + } + + done: + if (req) + kfree(req); + LEAVE(); + return ret; +} +#endif + +/** + * @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) +{ + int ret = 0; + moal_private *priv = (moal_private *) netdev_priv(dev); + char *buf = NULL; + int power_mode = 0; + int band = 0; + char *pband = NULL; + mlan_bss_info bss_info; + mlan_ds_get_signal signal; + mlan_rate_cfg_t rate; + char *pdata; + t_u8 country_code[COUNTRY_CODE_LEN]; + int len = 0; + ENTER(); + if (!(buf = kmalloc(dwrq->length + 1, GFP_KERNEL))) { + ret = -ENOMEM; + goto done; + } + memset(buf, 0, dwrq->length + 1); + if (copy_from_user(buf, dwrq->pointer, dwrq->length)) { + ret = -EFAULT; + goto done; + } + PRINTM(MIOCTL, "SIOCSIWPRIV requst = %s\n", buf); + if (strncmp(buf, "RSSILOW-THRESHOLD", strlen("RSSILOW-THRESHOLD")) == 0) { + pdata = buf + strlen("RSSILOW-THRESHOLD") + 1; + if (MLAN_STATUS_SUCCESS != woal_set_rssi_low_threshold(priv, pdata)) { + ret = -EFAULT; + goto done; + } + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "RSSI", strlen("RSSI")) == 0) { + if (MLAN_STATUS_SUCCESS != woal_get_bss_info(priv, + MOAL_IOCTL_WAIT, + &bss_info)) { + ret = -EFAULT; + goto done; + } + if (bss_info.media_connected) { + if (MLAN_STATUS_SUCCESS != + woal_get_signal_info(priv, MOAL_IOCTL_WAIT, &signal)) { + ret = -EFAULT; + goto done; + } + len = + sprintf(buf, "%s rssi %d\n", bss_info.ssid.ssid, + signal.bcn_rssi_avg) + 1; + } else { + len = sprintf(buf, "OK\n") + 1; + } + } else if (strncmp(buf, "LINKSPEED", strlen("LINKSPEED")) == 0) { + if (MLAN_STATUS_SUCCESS != + woal_set_get_data_rate(priv, MLAN_ACT_GET, &rate)) { + ret = -EFAULT; + goto done; + } + PRINTM(MIOCTL, "tx rate=%d\n", (int) rate.rate); + len = + sprintf(buf, "LinkSpeed %d\n", + (int) (rate.rate * 500000 / 1000000)) + 1; + } else if (strncmp(buf, "MACADDR", strlen("MACADDR")) == 0) { + len = + sprintf(buf, "Macaddr = %02X:%02X:%02X:%02X:%02X:%02X\n", + priv->current_addr[0], priv->current_addr[1], + priv->current_addr[2], priv->current_addr[3], + priv->current_addr[4], priv->current_addr[5]) + 1; + } else if (strncmp(buf, "GETPOWER", strlen("GETPOWER")) == 0) { + if (MLAN_STATUS_SUCCESS != woal_get_powermode(priv, &power_mode)) { + ret = -EFAULT; + goto done; + } + len = sprintf(buf, "powermode = %d\n", power_mode) + 1; + } else if (strncmp(buf, "SCAN-ACTIVE", strlen("SCAN-ACTIVE")) == 0) { + if (MLAN_STATUS_SUCCESS != + woal_set_scan_type(priv, MLAN_SCAN_TYPE_ACTIVE)) { + ret = -EFAULT; + goto done; + } + priv->scan_type = MLAN_SCAN_TYPE_ACTIVE; + PRINTM(MIOCTL, "Set Active Scan\n"); + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "SCAN-PASSIVE", strlen("SCAN-PASSIVE")) == 0) { + if (MLAN_STATUS_SUCCESS != + woal_set_scan_type(priv, MLAN_SCAN_TYPE_PASSIVE)) { + ret = -EFAULT; + goto done; + } + priv->scan_type = MLAN_SCAN_TYPE_PASSIVE; + PRINTM(MIOCTL, "Set Passive Scan\n"); + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "POWERMODE", strlen("POWERMODE")) == 0) { + pdata = buf + strlen("POWERMODE") + 1; + if (MLAN_STATUS_SUCCESS != woal_set_powermode(priv, pdata)) { + ret = -EFAULT; + goto done; + } + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "COUNTRY", strlen("COUNTRY")) == 0) { + memset(country_code, 0, sizeof(country_code)); + memcpy(country_code, buf + strlen("COUNTRY") + 1, + strlen(buf) - strlen("COUNTRY") - 1); + PRINTM(MIOCTL, "Set COUNTRY %s\n", country_code); + if (MLAN_STATUS_SUCCESS != woal_set_region_code(priv, country_code)) { + ret = -EFAULT; + goto done; + } + len = sprintf(buf, "OK\n") + 1; + } else if (memcmp(buf, WEXT_CSCAN_HEADER, WEXT_CSCAN_HEADER_SIZE) == 0) { + PRINTM(MIOCTL, "Set Combo Scan\n"); + if (MLAN_STATUS_SUCCESS != woal_set_combo_scan(priv, buf, dwrq->length)) { + ret = -EFAULT; + goto done; + } + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "GETBAND", strlen("GETBAND")) == 0) { + if (MLAN_STATUS_SUCCESS != woal_get_band(priv, &band)) { + ret = -EFAULT; + goto done; + } + len = sprintf(buf, "Band %d\n", band) + 1; + } else if (strncmp(buf, "SETBAND", strlen("SETBAND")) == 0) { + pband = buf + strlen("SETBAND") + 1; + if (MLAN_STATUS_SUCCESS != woal_set_band(priv, pband)) { + ret = -EFAULT; + goto done; + } + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "START", strlen("START")) == 0) { + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "STOP", strlen("STOP")) == 0) { + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "SETSUSPENDOPT", strlen("SETSUSPENDOPT")) == 0) { + /* it will be done by GUI */ + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "BTCOEXMODE", strlen("BTCOEXMODE")) == 0) { + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "BTCOEXSCAN-START", strlen("BTCOEXSCAN-START")) == + 0) { + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "BTCOEXSCAN-STOP", strlen("BTCOEXSCAN-STOP")) == 0) { + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "BTCOEXSCAN-START", strlen("BTCOEXSCAN-START")) == + 0) { + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "BTCOEXSCAN-STOP", strlen("BTCOEXSCAN-STOP")) == 0) { + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "BGSCAN-START", strlen("BGSCAN-START")) == 0) { + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "BGSCAN-CONFIG", strlen("BGSCAN-CONFIG")) == 0) { + if (MLAN_STATUS_SUCCESS != woal_set_bg_scan(priv, buf, dwrq->length)) { + ret = -EFAULT; + goto done; + } + priv->bg_scan_start = MTRUE; + priv->bg_scan_reported = MFALSE; + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "BGSCAN-STOP", strlen("BGSCAN-STOP")) == 0) { + if (priv->bg_scan_start && !priv->scan_cfg.rssi_threshold) { + if (MLAN_STATUS_SUCCESS != woal_stop_bg_scan(priv)) { + ret = -EFAULT; + goto done; + } + priv->bg_scan_start = MFALSE; + priv->bg_scan_reported = MFALSE; + } + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "RXFILTER-START", strlen("RXFILTER-START")) == 0) { +#ifdef MEF_CFG_RX_FILTER + if ((ret = woal_set_rxfilter(priv, MTRUE))) { + goto done; + } +#endif + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "RXFILTER-STOP", strlen("RXFILTER-STOP")) == 0) { +#ifdef MEF_CFG_RX_FILTER + if ((ret = woal_set_rxfilter(priv, MFALSE))) { + goto done; + } +#endif + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "RXFILTER-ADD", strlen("RXFILTER-ADD")) == 0) { + pdata = buf + strlen("RXFILTER-ADD") + 1; + if (MLAN_STATUS_SUCCESS != woal_add_rxfilter(priv, pdata)) { + ret = -EFAULT; + goto done; + } + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "RXFILTER-REMOVE", strlen("RXFILTER-REMOVE")) == 0) { + pdata = buf + strlen("RXFILTER-REMOVE") + 1; + if (MLAN_STATUS_SUCCESS != woal_remove_rxfilter(priv, pdata)) { + ret = -EFAULT; + goto done; + } + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "QOSINFO", strlen("QOSINFO")) == 0) { + pdata = buf + strlen("QOSINFO") + 1; + if (MLAN_STATUS_SUCCESS != woal_set_qos_cfg(priv, pdata)) { + ret = -EFAULT; + goto done; + } + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "SLEEPPD", strlen("SLEEPPD")) == 0) { + pdata = buf + strlen("SLEEPPD") + 1; + if (MLAN_STATUS_SUCCESS != woal_set_sleeppd(priv, pdata)) { + ret = -EFAULT; + goto done; + } + len = sprintf(buf, "OK\n") + 1; + } else { + PRINTM(MIOCTL, "Unknow PRIVATE command: %s, ignored\n", buf); + ret = -EFAULT; + goto done; + } + PRINTM(MIOCTL, "PRIV Command return: %s, length=%d\n", buf, len); + dwrq->length = (t_u16) len; + if (copy_to_user(dwrq->pointer, buf, dwrq->length)) { + ret = -EFAULT; + } + done: + if (buf) + kfree(buf); + LEAVE(); + return ret; +} + +/** + * @brief Scan Network + * + * @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_scan(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); + moal_handle *handle = priv->phandle; +#if WIRELESS_EXT >= 18 + struct iw_scan_req *req; + struct iw_point *dwrq = (struct iw_point *) vwrq; +#endif + mlan_802_11_ssid req_ssid; + + ENTER(); + if (handle->scan_pending_on_block == MTRUE) { + PRINTM(MINFO, "scan already in processing...\n"); + LEAVE(); + return ret; + } +#ifdef REASSOCIATION + if (MOAL_ACQ_SEMAPHORE_BLOCK(&handle->reassoc_sem)) { + PRINTM(MERROR, "Acquire semaphore error, woal_set_scan\n"); + LEAVE(); + return -EBUSY; + } +#endif /* REASSOCIATION */ + priv->report_scan_result = MTRUE; + + memset(&req_ssid, 0x00, sizeof(mlan_802_11_ssid)); + +#if WIRELESS_EXT >= 18 + if ((dwrq->flags & IW_SCAN_THIS_ESSID) && + (dwrq->length == sizeof(struct iw_scan_req))) { + req = (struct iw_scan_req *) extra; + + if (req->essid_len <= MLAN_MAX_SSID_LENGTH) { + + req_ssid.ssid_len = req->essid_len; + memcpy(req_ssid.ssid, (t_u8 *) req->essid, req->essid_len); + if (MLAN_STATUS_SUCCESS != + woal_request_scan(priv, MOAL_NO_WAIT, &req_ssid)) { + ret = -EFAULT; + goto done; + } + } + } else { +#endif + if (MLAN_STATUS_SUCCESS != woal_request_scan(priv, MOAL_NO_WAIT, NULL)) { + ret = -EFAULT; + goto done; + } +#if WIRELESS_EXT >= 18 + } +#endif + + if (priv->phandle->surprise_removed) { + ret = -EFAULT; + goto done; + } + + done: +#ifdef REASSOCIATION + MOAL_REL_SEMAPHORE(&handle->reassoc_sem); +#endif + + LEAVE(); + return ret; +} + +/** + * @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_802_11_ssid req_ssid; + mlan_ssid_bssid ssid_bssid; +#ifdef REASSOCIATION + moal_handle *handle = priv->phandle; + mlan_bss_info bss_info; +#endif + int ret = 0; + t_u32 mode = 0; + + ENTER(); + +#ifdef REASSOCIATION + /* Cancel re-association */ + priv->reassoc_required = MFALSE; + + if (MOAL_ACQ_SEMAPHORE_BLOCK(&handle->reassoc_sem)) { + PRINTM(MERROR, "Acquire semaphore error, woal_set_essid\n"); + LEAVE(); + return -EBUSY; + } +#endif /* REASSOCIATION */ + + /* Check the size of the string */ + if (dwrq->length > IW_ESSID_MAX_SIZE + 1) { + ret = -E2BIG; + goto setessid_ret; + } + if (priv->scan_type == MLAN_SCAN_TYPE_PASSIVE) + woal_set_scan_type(priv, MLAN_SCAN_TYPE_ACTIVE); + memset(&req_ssid, 0, sizeof(mlan_802_11_ssid)); + memset(&ssid_bssid, 0, sizeof(mlan_ssid_bssid)); + +#if WIRELESS_EXT > 20 + req_ssid.ssid_len = dwrq->length; +#else + req_ssid.ssid_len = dwrq->length - 1; +#endif + + /* + * Check if we asked for `any' or 'particular' + */ + if (!dwrq->flags) { +#ifdef REASSOCIATION + if (!req_ssid.ssid_len) { + memset(&priv->prev_ssid_bssid.ssid, 0x00, sizeof(mlan_802_11_ssid)); + memset(&priv->prev_ssid_bssid.bssid, 0x00, MLAN_MAC_ADDR_LENGTH); + goto setessid_ret; + } +#endif + /* Do normal SSID scanning */ + if (MLAN_STATUS_SUCCESS != + woal_request_scan(priv, MOAL_IOCTL_WAIT, NULL)) { + ret = -EFAULT; + goto setessid_ret; + } + } else { + /* Set the SSID */ + memcpy(req_ssid.ssid, extra, + MIN(req_ssid.ssid_len, MLAN_MAX_SSID_LENGTH)); + if (!req_ssid.ssid_len || (MFALSE == woal_ssid_valid(&req_ssid))) { + PRINTM(MERROR, "Invalid SSID - aborting set_essid\n"); + ret = -EINVAL; + goto setessid_ret; + } + + PRINTM(MINFO, "Requested new SSID = %s\n", (char *) req_ssid.ssid); + memcpy(&ssid_bssid.ssid, &req_ssid, sizeof(mlan_802_11_ssid)); + + if (dwrq->flags != 0xFFFF) { + if (MLAN_STATUS_SUCCESS != woal_find_essid(priv, &ssid_bssid)) { + /* Do specific SSID scanning */ + if (MLAN_STATUS_SUCCESS != + woal_request_scan(priv, MOAL_IOCTL_WAIT, &req_ssid)) { + ret = -EFAULT; + goto setessid_ret; + } + } + } + + } + + /* disconnect before try to associate */ + woal_disconnect(priv, MOAL_IOCTL_WAIT, NULL); + mode = woal_get_mode(priv, MOAL_IOCTL_WAIT); + + if (mode != IW_MODE_ADHOC) { + if (MLAN_STATUS_SUCCESS != + woal_find_best_network(priv, MOAL_IOCTL_WAIT, &ssid_bssid)) { + ret = -EFAULT; + goto setessid_ret; + } + } else if (MLAN_STATUS_SUCCESS != + woal_find_best_network(priv, MOAL_IOCTL_WAIT, &ssid_bssid)) + /* Adhoc start, Check the channel command */ + woal_11h_channel_check_ioctl(priv); + + /* Connect to BSS by ESSID */ + memset(&ssid_bssid.bssid, 0, MLAN_MAC_ADDR_LENGTH); + + if (MLAN_STATUS_SUCCESS != woal_bss_start(priv, + MOAL_IOCTL_WAIT, &ssid_bssid)) { + ret = -EFAULT; + goto setessid_ret; + } +#ifdef REASSOCIATION + memset(&bss_info, 0, sizeof(bss_info)); + if (MLAN_STATUS_SUCCESS != woal_get_bss_info(priv, + MOAL_IOCTL_WAIT, &bss_info)) { + ret = -EFAULT; + goto setessid_ret; + } + memcpy(&priv->prev_ssid_bssid.ssid, &bss_info.ssid, + sizeof(mlan_802_11_ssid)); + memcpy(&priv->prev_ssid_bssid.bssid, &bss_info.bssid, MLAN_MAC_ADDR_LENGTH); +#endif /* REASSOCIATION */ + + setessid_ret: + if (priv->scan_type == MLAN_SCAN_TYPE_PASSIVE) + woal_set_scan_type(priv, MLAN_SCAN_TYPE_PASSIVE); +#ifdef REASSOCIATION + MOAL_REL_SEMAPHORE(&handle->reassoc_sem); +#endif + + 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_bss_info bss_info; + int ret = 0; + + ENTER(); + + memset(&bss_info, 0, sizeof(bss_info)); + + if (MLAN_STATUS_SUCCESS != + woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info)) { + ret = -EFAULT; + goto done; + } + + if (bss_info.media_connected) { + dwrq->length = MIN(dwrq->length, bss_info.ssid.ssid_len); + memcpy(extra, bss_info.ssid.ssid, dwrq->length); + } else + dwrq->length = 0; + + if (bss_info.scan_table_idx) + dwrq->flags = (bss_info.scan_table_idx + 1) & IW_ENCODE_INDEX; + else + dwrq->flags = 1; + + done: + LEAVE(); + return ret; +} + +/** + * @brief Retrieve the scan table entries via wireless tools IOCTL call + * + * @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_scan(struct net_device *dev, struct iw_request_info *info, + struct iw_point *dwrq, char *extra) +{ + moal_private *priv = (moal_private *) netdev_priv(dev); + int ret = 0; + char *current_ev = extra; + char *end_buf = extra + IW_SCAN_MAX_DATA; + char *current_val; /* For rates */ + struct iw_event iwe; /* Temporary buffer */ + unsigned int i; + unsigned int j; + mlan_scan_resp scan_resp; + mlan_bss_info bss_info; + BSSDescriptor_t *scan_table; + mlan_ds_get_signal rssi; + t_u16 buf_size = 16 + 256 * 2; + char *buf = NULL; + char *ptr; +#if WIRELESS_EXT >= 18 + t_u8 *praw_data; +#endif + int beacon_size; + t_u8 *pbeacon; + IEEEtypes_ElementId_e element_id; + t_u8 element_len; + + ENTER(); + + if (priv->phandle->scan_pending_on_block == MTRUE) { + LEAVE(); + return -EAGAIN; + } + + if (!(buf = kmalloc((buf_size), GFP_KERNEL))) { + PRINTM(MERROR, "Cannot allocate buffer!\n"); + ret = -EFAULT; + goto done; + } + + memset(&bss_info, 0, sizeof(bss_info)); + if (MLAN_STATUS_SUCCESS != + woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info)) { + ret = -EFAULT; + goto done; + } + memset(&scan_resp, 0, sizeof(scan_resp)); + if (MLAN_STATUS_SUCCESS != woal_get_scan_table(priv, + MOAL_IOCTL_WAIT, + &scan_resp)) { + ret = -EFAULT; + goto done; + } + scan_table = (BSSDescriptor_t *) scan_resp.pscan_table; + if (dwrq->length) + end_buf = extra + dwrq->length; + if (priv->media_connected == MTRUE) { + PRINTM(MINFO, "Current Ssid: %-32s\n", bss_info.ssid.ssid); + } + PRINTM(MINFO, "Scan: Get: NumInScanTable = %d\n", + (int) scan_resp.num_in_scan_table); + +#if WIRELESS_EXT > 13 + /* The old API using SIOCGIWAPLIST had a hard limit of IW_MAX_AP. The new + API using SIOCGIWSCAN is only limited by buffer size WE-14 -> WE-16 the + buffer is limited to IW_SCAN_MAX_DATA bytes which is 4096. */ + for (i = 0; i < scan_resp.num_in_scan_table; i++) { + if ((current_ev + MAX_SCAN_CELL_SIZE) >= end_buf) { + PRINTM(MINFO, "i=%d break out: current_ev=%p end_buf=%p " + "MAX_SCAN_CELL_SIZE=%d\n", + i, current_ev, end_buf, (t_u32) MAX_SCAN_CELL_SIZE); + ret = -E2BIG; + break; + } + if (!scan_table[i].freq) { + PRINTM(MERROR, "Invalid channel number %d\n", + (int) scan_table[i].channel); + continue; + } + PRINTM(MINFO, "i=%d Ssid: %-32s\n", i, scan_table[i].ssid.ssid); + + /* check ssid is valid or not, ex. hidden ssid will be filter out */ + if (woal_ssid_valid(&scan_table[i].ssid) == MFALSE) { + continue; + } + + /* First entry *MUST* be the AP MAC address */ + iwe.cmd = SIOCGIWAP; + iwe.u.ap_addr.sa_family = ARPHRD_ETHER; + memcpy(iwe.u.ap_addr.sa_data, &scan_table[i].mac_address, ETH_ALEN); + + iwe.len = IW_EV_ADDR_LEN; + current_ev = + IWE_STREAM_ADD_EVENT(info, current_ev, end_buf, &iwe, iwe.len); + + /* Add the ESSID */ + iwe.u.data.length = scan_table[i].ssid.ssid_len; + + if (iwe.u.data.length > 32) { + iwe.u.data.length = 32; + } + + iwe.cmd = SIOCGIWESSID; + iwe.u.essid.flags = (i + 1) & IW_ENCODE_INDEX; + iwe.len = IW_EV_POINT_LEN + iwe.u.data.length; + current_ev = + IWE_STREAM_ADD_POINT(info, current_ev, end_buf, &iwe, + (t_s8 *) scan_table[i].ssid.ssid); + + /* Add mode */ + iwe.cmd = SIOCGIWMODE; + if (scan_table[i].bss_mode == MLAN_BSS_MODE_IBSS) + iwe.u.mode = IW_MODE_ADHOC; + else if (scan_table[i].bss_mode == MLAN_BSS_MODE_INFRA) + iwe.u.mode = IW_MODE_MASTER; + else + iwe.u.mode = IW_MODE_AUTO; + + iwe.len = IW_EV_UINT_LEN; + current_ev = + IWE_STREAM_ADD_EVENT(info, current_ev, end_buf, &iwe, iwe.len); + + /* Frequency */ + iwe.cmd = SIOCGIWFREQ; + iwe.u.freq.m = (long) scan_table[i].freq * 100000; + iwe.u.freq.e = 1; + iwe.u.freq.flags = IW_FREQ_FIXED; + iwe.len = IW_EV_FREQ_LEN; + current_ev = + IWE_STREAM_ADD_EVENT(info, current_ev, end_buf, &iwe, iwe.len); + + memset(&iwe, 0, sizeof(iwe)); + /* Add quality statistics */ + iwe.cmd = IWEVQUAL; + iwe.u.qual.level = SCAN_RSSI(scan_table[i].rssi); + if (!bss_info.bcn_nf_last) { + iwe.u.qual.noise = MRVDRV_NF_DEFAULT_SCAN_VALUE; + } else { + iwe.u.qual.noise = bss_info.bcn_nf_last; + } + if ((bss_info.bss_mode == MLAN_BSS_MODE_IBSS) && + !woal_ssid_cmp(&bss_info.ssid, &scan_table[i].ssid) + && bss_info.adhoc_state == ADHOC_STARTED) { + memset(&rssi, 0, sizeof(mlan_ds_get_signal)); + if (MLAN_STATUS_SUCCESS != + woal_get_signal_info(priv, MOAL_IOCTL_WAIT, &rssi)) { + ret = -EFAULT; + break; + } + iwe.u.qual.level = rssi.data_rssi_avg; + } + iwe.u.qual.qual = + woal_rssi_to_quality((t_s16) (iwe.u.qual.level - 0x100)); + iwe.len = IW_EV_QUAL_LEN; + current_ev = + IWE_STREAM_ADD_EVENT(info, current_ev, end_buf, &iwe, iwe.len); + + /* Add encryption capability */ + iwe.cmd = SIOCGIWENCODE; + if (scan_table[i].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; + current_ev = + IWE_STREAM_ADD_POINT(info, current_ev, end_buf, &iwe, NULL); + + current_val = current_ev + IW_EV_LCP_LEN; + + iwe.cmd = SIOCGIWRATE; + + iwe.u.bitrate.fixed = 0; + iwe.u.bitrate.disabled = 0; + iwe.u.bitrate.value = 0; + + /* Bit rate given in 500 kb/s units (+ 0x80) */ + for (j = 0; j < sizeof(scan_table[i].supported_rates); j++) { + if (!scan_table[i].supported_rates[j]) { + break; + } + + iwe.u.bitrate.value = + (scan_table[i].supported_rates[j] & 0x7f) * 500000; + iwe.len = IW_EV_PARAM_LEN; + current_val = + IWE_STREAM_ADD_VALUE(info, current_ev, current_val, end_buf, + &iwe, iwe.len); + + } + if ((bss_info.bss_mode == MLAN_BSS_MODE_IBSS) && + !woal_ssid_cmp(&bss_info.ssid, &scan_table[i].ssid) + && bss_info.adhoc_state == ADHOC_STARTED) { + iwe.u.bitrate.value = 22 * 500000; + iwe.len = IW_EV_PARAM_LEN; + current_val = + IWE_STREAM_ADD_VALUE(info, current_ev, current_val, end_buf, + &iwe, iwe.len); + } + + /* Check if an event is added */ + if ((unsigned int) (current_val - current_ev) >= IW_EV_PARAM_LEN) + current_ev = current_val; + + /* Beacon Interval */ + memset(&iwe, 0, sizeof(iwe)); + memset(buf, 0, buf_size); + ptr = buf; + ptr += sprintf(ptr, "Beacon interval=%d", scan_table[i].beacon_period); + + iwe.u.data.length = strlen(buf); + iwe.cmd = IWEVCUSTOM; + iwe.len = IW_EV_POINT_LEN + iwe.u.data.length; + current_ev = IWE_STREAM_ADD_POINT(info, current_ev, end_buf, &iwe, buf); + current_val = current_ev + IW_EV_LCP_LEN + strlen(buf); + + /* Parse and send the IEs */ + pbeacon = scan_table[i].pbeacon_buf; + beacon_size = scan_table[i].beacon_buf_size; + + /* Skip time stamp, beacon interval and capability */ + if (pbeacon) { + pbeacon += sizeof(scan_table[i].beacon_period) + + sizeof(scan_table[i].time_stamp) + + sizeof(scan_table[i].cap_info); + beacon_size -= sizeof(scan_table[i].beacon_period) + + sizeof(scan_table[i].time_stamp) + + sizeof(scan_table[i].cap_info); + + while ((unsigned int) beacon_size >= sizeof(IEEEtypes_Header_t)) { + element_id = (IEEEtypes_ElementId_e) (*(t_u8 *) pbeacon); + element_len = *((t_u8 *) pbeacon + 1); + if ((unsigned int) beacon_size < + (unsigned int) element_len + sizeof(IEEEtypes_Header_t)) { + PRINTM(MERROR, + "Get scan: Error in processing IE, " + "bytes left < IE length\n"); + break; + } + + switch (element_id) { +#if WIRELESS_EXT >= 18 + case VENDOR_SPECIFIC_221: + case RSN_IE: + case WAPI_IE: + praw_data = (t_u8 *) pbeacon; + memset(&iwe, 0, sizeof(iwe)); + memset(buf, 0, buf_size); + ptr = buf; + memcpy(buf, praw_data, + element_len + sizeof(IEEEtypes_Header_t)); + iwe.cmd = IWEVGENIE; + iwe.u.data.length = + element_len + sizeof(IEEEtypes_Header_t); + iwe.len = IW_EV_POINT_LEN + iwe.u.data.length; + current_ev = + IWE_STREAM_ADD_POINT(info, current_ev, end_buf, &iwe, + buf); + current_val = current_ev + IW_EV_LCP_LEN + strlen(buf); + break; +#endif + default: + break; + } + pbeacon += element_len + sizeof(IEEEtypes_Header_t); + beacon_size -= element_len + sizeof(IEEEtypes_Header_t); + } + } +#if WIRELESS_EXT > 14 + memset(&iwe, 0, sizeof(iwe)); + memset(buf, 0, buf_size); + ptr = buf; + ptr += sprintf(ptr, "band="); + memset(&iwe, 0, sizeof(iwe)); + if (scan_table[i].bss_band == BAND_A) + ptr += sprintf(ptr, "a"); + else + ptr += sprintf(ptr, "bg"); + iwe.u.data.length = strlen(buf); + PRINTM(MINFO, "iwe.u.data.length %d\n", iwe.u.data.length); + PRINTM(MINFO, "BUF: %s \n", buf); + iwe.cmd = IWEVCUSTOM; + iwe.len = IW_EV_POINT_LEN + iwe.u.data.length; + current_ev = IWE_STREAM_ADD_POINT(info, current_ev, end_buf, &iwe, buf); + current_val = current_ev + IW_EV_LCP_LEN + strlen(buf); +#endif + current_val = current_ev + IW_EV_LCP_LEN; + + /* + * Check if we added any event + */ + if ((unsigned int) (current_val - current_ev) > IW_EV_LCP_LEN) + current_ev = current_val; + } + + dwrq->length = (current_ev - extra); + dwrq->flags = 0; +#endif + + done: + if (buf) + kfree(buf); + LEAVE(); + return ret; +} + +/** + * 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) woal_set_sens, /* SIOCSIWSENS */ + (iw_handler) woal_get_sens, /* 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 + iw_handler_set_spy, /* SIOCSIWSPY */ + iw_handler_get_spy, /* SIOCGIWSPY */ + iw_handler_set_thrspy, /* SIOCSIWTHRSPY */ + iw_handler_get_thrspy, /* SIOCGIWTHRSPY */ +#else /* WIRELESS_EXT > 15 */ +#ifdef WIRELESS_SPY + (iw_handler) NULL, /* SIOCSIWSPY */ + (iw_handler) NULL, /* SIOCGIWSPY */ +#else /* WIRELESS_SPY */ + (iw_handler) NULL, /* SIOCSIWSPY */ + (iw_handler) NULL, /* SIOCGIWSPY */ +#endif /* WIRELESS_SPY */ + (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) woal_set_scan, /* SIOCSIWSCAN */ + (iw_handler) woal_get_scan, /* 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) woal_set_nick, /* SIOCSIWNICKN */ + (iw_handler) woal_get_nick, /* SIOCGIWNICKN */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) woal_set_rate, /* SIOCSIWRATE */ + (iw_handler) woal_get_rate, /* SIOCGIWRATE */ + (iw_handler) woal_set_rts, /* SIOCSIWRTS */ + (iw_handler) woal_get_rts, /* SIOCGIWRTS */ + (iw_handler) woal_set_frag, /* SIOCSIWFRAG */ + (iw_handler) woal_get_frag, /* SIOCGIWFRAG */ + (iw_handler) woal_set_txpow, /* SIOCSIWTXPOW */ + (iw_handler) woal_get_txpow, /* SIOCGIWTXPOW */ + (iw_handler) woal_set_retry, /* SIOCSIWRETRY */ + (iw_handler) woal_get_retry, /* SIOCGIWRETRY */ + (iw_handler) woal_set_encode, /* SIOCSIWENCODE */ + (iw_handler) woal_get_encode, /* SIOCGIWENCODE */ + (iw_handler) woal_set_power, /* SIOCSIWPOWER */ + (iw_handler) woal_get_power, /* 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 */ + (iw_handler) woal_set_pmksa, /* SIOCSIWPMKSA */ +#endif /* WIRELESSS_EXT >= 18 */ +}; + +/** + * iwpriv settable callbacks + */ +static const iw_handler woal_private_handler[] = { + NULL, /* SIOCIWFIRSTPRIV */ +}; +#endif /* STA_SUPPORT */ + +/******************************************************** + Global Functions +********************************************************/ + +#if WIRELESS_EXT > 14 + +/** + * @brief This function sends customized event to application. + * + * @param priv A pointer to moal_private structure + * @param str A pointer to event string + * + * @return N/A + */ +void +woal_send_iwevcustom_event(moal_private * priv, t_s8 * str) +{ + union iwreq_data iwrq; + char buf[IW_CUSTOM_MAX]; + + ENTER(); + + memset(&iwrq, 0, sizeof(union iwreq_data)); + memset(buf, 0, sizeof(buf)); + + snprintf(buf, sizeof(buf) - 1, "%s", str); + + iwrq.data.pointer = buf; + iwrq.data.length = strlen(buf) + 1; + + /* Send Event to upper layer */ + wireless_send_event(priv->netdev, IWEVCUSTOM, &iwrq, buf); + PRINTM(MINFO, "Wireless event %s is sent to application\n", str); + + LEAVE(); + return; +} +#endif + +#if WIRELESS_EXT >= 18 +/** + * @brief This function sends mic error event to application. + * + * @param priv A pointer to moal_private structure + * @param event MIC MERROR EVENT. + * + * @return N/A + */ +void +woal_send_mic_error_event(moal_private * priv, t_u32 event) +{ + union iwreq_data iwrq; + struct iw_michaelmicfailure mic; + + ENTER(); + + memset(&iwrq, 0, sizeof(iwrq)); + memset(&mic, 0, sizeof(mic)); + if (event == MLAN_EVENT_ID_FW_MIC_ERR_UNI) + mic.flags = IW_MICFAILURE_PAIRWISE; + else + mic.flags = IW_MICFAILURE_GROUP; + iwrq.data.pointer = &mic; + iwrq.data.length = sizeof(mic); + + wireless_send_event(priv->netdev, IWEVMICHAELMICFAILURE, &iwrq, + (char *) &mic); + + LEAVE(); + return; +} +#endif + +#ifdef STA_SUPPORT +/** + * @brief Set Radio On/OFF + * + * @param priv A pointer to moal_private structure + * @param option Radio Option + * + * @return 0 --success, otherwise fail + */ +int +woal_set_radio(moal_private * priv, t_u8 option) +{ + int ret = 0; + mlan_ds_radio_cfg *radio = NULL; + mlan_ioctl_req *req = NULL; + ENTER(); + if ((option != 0) && (option != 1)) { + ret = -EINVAL; + goto done; + } + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_radio_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + radio = (mlan_ds_radio_cfg *) req->pbuf; + radio->sub_command = MLAN_OID_RADIO_CTRL; + req->req_id = MLAN_IOCTL_RADIO_CFG; + req->action = MLAN_ACT_SET; + radio->param.radio_on_off = option; + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto done; + } + done: + if (req) + kfree(req); + LEAVE(); + return ret; +} + +/** wlan_handler_def */ +struct iw_handler_def woal_handler_def = { + num_standard:sizeof(woal_handler) / sizeof(iw_handler), + num_private:sizeof(woal_private_handler) / sizeof(iw_handler), + num_private_args:sizeof(woal_private_args) / sizeof(struct iw_priv_args), + standard:(iw_handler *) woal_handler, + private:(iw_handler *) woal_private_handler, + private_args:(struct iw_priv_args *) woal_private_args, +#if WIRELESS_EXT > 20 + get_wireless_stats:woal_get_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_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.status = woal_get_mode(priv, wait_option); + priv->w_stats.discard.retries = priv->stats.tx_errors; + priv->w_stats.qual.qual = 0; + + /* Send RSSI command to get beacon RSSI/NF, valid only if associated */ + if (priv->media_connected == MTRUE) { + woal_get_signal_info(priv, wait_option, NULL); + priv->w_stats.qual.qual = woal_rssi_to_quality((t_s16) + (priv->w_stats.qual. + level - 0x100)); + } +#if WIRELESS_EXT > 18 + priv->w_stats.qual.updated |= (IW_QUAL_ALL_UPDATED | IW_QUAL_DBM); +#else + priv->w_stats.qual.updated |= 7; +#endif + if (!priv->w_stats.qual.noise && priv->media_connected == MTRUE) + priv->w_stats.qual.noise = MRVDRV_NF_DEFAULT_SCAN_VALUE; + + PRINTM(MINFO, "Link Quality = %#x\n", priv->w_stats.qual.qual); + PRINTM(MINFO, "Signal Level = %#x\n", priv->w_stats.qual.level); + PRINTM(MINFO, "Noise = %#x\n", priv->w_stats.qual.noise); + priv->w_stats.discard.code = 0; + woal_get_stats_info(priv, wait_option, NULL); + + LEAVE(); + return &priv->w_stats; +} +#endif /* STA_SUPPORT */ |