diff options
Diffstat (limited to 'drivers/net/wireless/sd8797/mlinux/moal_uap.c')
-rw-r--r-- | drivers/net/wireless/sd8797/mlinux/moal_uap.c | 2692 |
1 files changed, 2692 insertions, 0 deletions
diff --git a/drivers/net/wireless/sd8797/mlinux/moal_uap.c b/drivers/net/wireless/sd8797/mlinux/moal_uap.c new file mode 100644 index 000000000000..7dac877abe00 --- /dev/null +++ b/drivers/net/wireless/sd8797/mlinux/moal_uap.c @@ -0,0 +1,2692 @@ +/** @file moal_uap.c + * + * @brief This file contains the major functions in UAP + * driver. + * + * Copyright (C) 2008-2012, 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" +#include "moal_uap.h" +#include "moal_sdio.h" +#include "moal_eth_ioctl.h" +#if defined(STA_CFG80211) && defined(UAP_CFG80211) +#include "moal_cfg80211.h" +#endif + +/******************************************************** + Local Variables +********************************************************/ + +/******************************************************** + Global Variables +********************************************************/ + +/******************************************************** + Local Functions +********************************************************/ +/** + * @brief uap addba parameter handler + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int +woal_uap_addba_param(struct net_device *dev, struct ifreq *req) +{ + moal_private *priv = (moal_private *) netdev_priv(dev); + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_11n_cfg *cfg_11n = NULL; + addba_param param; + int ret = 0; + + ENTER(); + memset(¶m, 0, sizeof(param)); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "uap_addba_param() corrupt data\n"); + ret = -EFAULT; + goto done; + } + if (copy_from_user(¶m, req->ifr_data, sizeof(param))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + PRINTM(MIOCTL, + "addba param: action=%d, timeout=%d, txwinsize=%d, rxwinsize=%d txamsdu=%d rxamsdu=%d\n", + (int) param.action, (int) param.timeout, (int) param.txwinsize, + (int) param.rxwinsize, (int) param.txamsdu, (int) param.rxamsdu); + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); + if (ioctl_req == NULL) { + LEAVE(); + return -ENOMEM; + } + cfg_11n = (mlan_ds_11n_cfg *) ioctl_req->pbuf; + cfg_11n->sub_command = MLAN_OID_11N_CFG_ADDBA_PARAM; + ioctl_req->req_id = MLAN_IOCTL_11N_CFG; + + if (!param.action) { + /* Get addba param from MLAN */ + ioctl_req->action = MLAN_ACT_GET; + } else { + /* Set addba param in MLAN */ + ioctl_req->action = MLAN_ACT_SET; + cfg_11n->param.addba_param.timeout = param.timeout; + cfg_11n->param.addba_param.txwinsize = param.txwinsize; + cfg_11n->param.addba_param.rxwinsize = param.rxwinsize; + cfg_11n->param.addba_param.txamsdu = param.txamsdu; + cfg_11n->param.addba_param.rxamsdu = param.rxamsdu; + } + if (MLAN_STATUS_SUCCESS != + woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto done; + } + param.timeout = cfg_11n->param.addba_param.timeout; + param.txwinsize = cfg_11n->param.addba_param.txwinsize; + param.rxwinsize = cfg_11n->param.addba_param.rxwinsize; + param.txamsdu = cfg_11n->param.addba_param.txamsdu; + param.rxamsdu = cfg_11n->param.addba_param.rxamsdu; + + /* Copy to user */ + if (copy_to_user(req->ifr_data, ¶m, sizeof(param))) { + PRINTM(MERROR, "Copy to user failed!\n"); + ret = -EFAULT; + goto done; + } + done: + if (ioctl_req) + kfree(ioctl_req); + LEAVE(); + return ret; +} + +/** + * @brief uap aggr priority tbl + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int +woal_uap_aggr_priotbl(struct net_device *dev, struct ifreq *req) +{ + moal_private *priv = (moal_private *) netdev_priv(dev); + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_11n_cfg *cfg_11n = NULL; + aggr_prio_tbl param; + int ret = 0; + int i = 0; + + ENTER(); + memset(¶m, 0, sizeof(param)); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "woal_uap_aggr_priotbl() corrupt data\n"); + ret = -EFAULT; + goto done; + } + if (copy_from_user(¶m, req->ifr_data, sizeof(param))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + DBG_HEXDUMP(MCMD_D, "aggr_prio_tbl", (t_u8 *) & param, sizeof(param)); + + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); + if (ioctl_req == NULL) { + LEAVE(); + return -ENOMEM; + } + cfg_11n = (mlan_ds_11n_cfg *) ioctl_req->pbuf; + cfg_11n->sub_command = MLAN_OID_11N_CFG_AGGR_PRIO_TBL; + ioctl_req->req_id = MLAN_IOCTL_11N_CFG; + + if (!param.action) { + /* Get aggr_prio_tbl from MLAN */ + ioctl_req->action = MLAN_ACT_GET; + } else { + /* Set aggr_prio_tbl in MLAN */ + ioctl_req->action = MLAN_ACT_SET; + for (i = 0; i < MAX_NUM_TID; i++) { + cfg_11n->param.aggr_prio_tbl.ampdu[i] = param.ampdu[i]; + cfg_11n->param.aggr_prio_tbl.amsdu[i] = param.amsdu[i]; + } + } + if (MLAN_STATUS_SUCCESS != + woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto done; + } + for (i = 0; i < MAX_NUM_TID; i++) { + param.ampdu[i] = cfg_11n->param.aggr_prio_tbl.ampdu[i]; + param.amsdu[i] = cfg_11n->param.aggr_prio_tbl.amsdu[i]; + } + /* Copy to user */ + if (copy_to_user(req->ifr_data, ¶m, sizeof(param))) { + PRINTM(MERROR, "Copy to user failed!\n"); + ret = -EFAULT; + goto done; + } + done: + if (ioctl_req) + kfree(ioctl_req); + LEAVE(); + return ret; +} + +/** + * @brief uap addba reject tbl + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int +woal_uap_addba_reject(struct net_device *dev, struct ifreq *req) +{ + moal_private *priv = (moal_private *) netdev_priv(dev); + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_11n_cfg *cfg_11n = NULL; + addba_reject_para param; + int ret = 0; + int i = 0; + + ENTER(); + memset(¶m, 0, sizeof(param)); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "woal_uap_addba_reject() corrupt data\n"); + ret = -EFAULT; + goto done; + } + if (copy_from_user(¶m, req->ifr_data, sizeof(param))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + DBG_HEXDUMP(MCMD_D, "addba_reject tbl", (t_u8 *) & param, sizeof(param)); + + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); + if (ioctl_req == NULL) { + LEAVE(); + return -ENOMEM; + } + cfg_11n = (mlan_ds_11n_cfg *) ioctl_req->pbuf; + cfg_11n->sub_command = MLAN_OID_11N_CFG_ADDBA_REJECT; + ioctl_req->req_id = MLAN_IOCTL_11N_CFG; + + if (!param.action) { + /* Get addba_reject tbl from MLAN */ + ioctl_req->action = MLAN_ACT_GET; + } else { + /* Set addba_reject tbl in MLAN */ + ioctl_req->action = MLAN_ACT_SET; + for (i = 0; i < MAX_NUM_TID; i++) { + cfg_11n->param.addba_reject[i] = param.addba_reject[i]; + } + } + if (MLAN_STATUS_SUCCESS != + woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto done; + } + for (i = 0; i < MAX_NUM_TID; i++) { + param.addba_reject[i] = cfg_11n->param.addba_reject[i]; + } + /* Copy to user */ + if (copy_to_user(req->ifr_data, ¶m, sizeof(param))) { + PRINTM(MERROR, "Copy to user failed!\n"); + ret = -EFAULT; + goto done; + } + done: + if (ioctl_req) + kfree(ioctl_req); + LEAVE(); + return ret; +} + +/** + * @brief uap get_fw_info handler + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int +woal_uap_get_fw_info(struct net_device *dev, struct ifreq *req) +{ + moal_private *priv = (moal_private *) netdev_priv(dev); + fw_info fw; + mlan_fw_info fw_info; + int ret = 0; + + ENTER(); + memset(&fw, 0, sizeof(fw)); + memset(&fw_info, 0, sizeof(fw_info)); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "woal_uap_get_fw_info() corrupt data\n"); + ret = -EFAULT; + goto done; + } + if (copy_from_user(&fw, req->ifr_data, sizeof(fw))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + if (MLAN_STATUS_SUCCESS != + woal_request_get_fw_info(priv, MOAL_IOCTL_WAIT, &fw_info)) { + ret = -EFAULT; + goto done; + } + fw.fw_release_number = fw_info.fw_ver; + fw.hw_dev_mcs_support = fw_info.hw_dev_mcs_support; + /* Copy to user */ + if (copy_to_user(req->ifr_data, &fw, sizeof(fw))) { + PRINTM(MERROR, "Copy to user failed!\n"); + ret = -EFAULT; + goto done; + } + done: + LEAVE(); + return ret; +} + +/** + * @brief configure deep sleep + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int +woal_uap_deep_sleep(struct net_device *dev, struct ifreq *req) +{ + moal_private *priv = (moal_private *) netdev_priv(dev); + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_pm_cfg *pm = NULL; + deep_sleep_para param; + int ret = 0; + + ENTER(); + memset(¶m, 0, sizeof(param)); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "woal_uap_deep_sleep() corrupt data\n"); + ret = -EFAULT; + goto done; + } + if (copy_from_user(¶m, req->ifr_data, sizeof(param))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + DBG_HEXDUMP(MCMD_D, "deep_sleep_para", (t_u8 *) & param, sizeof(param)); + + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_pm_cfg)); + if (ioctl_req == NULL) { + LEAVE(); + return -ENOMEM; + } + pm = (mlan_ds_pm_cfg *) ioctl_req->pbuf; + pm->sub_command = MLAN_OID_PM_CFG_DEEP_SLEEP; + ioctl_req->req_id = MLAN_IOCTL_PM_CFG; + + if (!param.action) { + /* Get deep_sleep status from MLAN */ + ioctl_req->action = MLAN_ACT_GET; + } else { + /* Set deep_sleep in MLAN */ + ioctl_req->action = MLAN_ACT_SET; + if (param.deep_sleep == MTRUE) { + pm->param.auto_deep_sleep.auto_ds = DEEP_SLEEP_ON; + pm->param.auto_deep_sleep.idletime = param.idle_time; + } else { + pm->param.auto_deep_sleep.auto_ds = DEEP_SLEEP_OFF; + } + } + if (MLAN_STATUS_SUCCESS != + woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto done; + } + if (pm->param.auto_deep_sleep.auto_ds == DEEP_SLEEP_ON) + param.deep_sleep = MTRUE; + else + param.deep_sleep = MFALSE; + param.idle_time = pm->param.auto_deep_sleep.idletime; + /* Copy to user */ + if (copy_to_user(req->ifr_data, ¶m, sizeof(param))) { + PRINTM(MERROR, "Copy to user failed!\n"); + ret = -EFAULT; + goto done; + } + done: + if (ioctl_req) + kfree(ioctl_req); + LEAVE(); + return ret; +} + +/** + * @brief configure tx_pause settings + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int +woal_uap_txdatapause(struct net_device *dev, struct ifreq *req) +{ + moal_private *priv = (moal_private *) netdev_priv(dev); + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_misc_cfg *misc = NULL; + tx_data_pause_para param; + int ret = 0; + + ENTER(); + memset(¶m, 0, sizeof(param)); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "woal_uap_txdatapause corrupt data\n"); + ret = -EFAULT; + goto done; + } + if (copy_from_user(¶m, req->ifr_data, sizeof(param))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + DBG_HEXDUMP(MCMD_D, "tx_data_pause_para", (t_u8 *) & param, sizeof(param)); + + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (ioctl_req == NULL) { + LEAVE(); + return -ENOMEM; + } + misc = (mlan_ds_misc_cfg *) ioctl_req->pbuf; + misc->sub_command = MLAN_OID_MISC_TX_DATAPAUSE; + ioctl_req->req_id = MLAN_IOCTL_MISC_CFG; + + if (!param.action) { + /* Get Tx data pause status from MLAN */ + ioctl_req->action = MLAN_ACT_GET; + } else { + /* Set Tx data pause in MLAN */ + ioctl_req->action = MLAN_ACT_SET; + misc->param.tx_datapause.tx_pause = param.txpause; + misc->param.tx_datapause.tx_buf_cnt = param.txbufcnt; + } + if (MLAN_STATUS_SUCCESS != + woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto done; + } + + param.txpause = misc->param.tx_datapause.tx_pause; + param.txbufcnt = misc->param.tx_datapause.tx_buf_cnt; + + /* Copy to user */ + if (copy_to_user(req->ifr_data, ¶m, sizeof(param))) { + PRINTM(MERROR, "Copy to user failed!\n"); + ret = -EFAULT; + goto done; + } + done: + if (ioctl_req) + kfree(ioctl_req); + LEAVE(); + return ret; +} + +/** + * @brief uap sdcmd52rw ioctl handler + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int +woal_uap_sdcmd52_rw(struct net_device *dev, struct ifreq *req) +{ + moal_private *priv = (moal_private *) netdev_priv(dev); + sdcmd52_para param; + t_u8 func, data = 0; + int ret = 0, reg; + + ENTER(); + memset(¶m, 0, sizeof(param)); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "woal_uap_sdcmd52_rw() corrupt data\n"); + ret = -EFAULT; + goto done; + } + if (copy_from_user(¶m, req->ifr_data, sizeof(param))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + + func = (t_u8) param.cmd52_params[0]; + reg = (t_u32) param.cmd52_params[1]; + + if (!param.action) { + PRINTM(MINFO, "Cmd52 read, func=%d, reg=0x%08X\n", func, reg); + sdio_claim_host(((struct sdio_mmc_card *) priv->phandle->card)->func); + if (func) + data = + sdio_readb(((struct sdio_mmc_card *) priv->phandle->card)->func, + reg, &ret); + else + data = + sdio_f0_readb(((struct sdio_mmc_card *) priv->phandle->card)-> + func, reg, &ret); + sdio_release_host(((struct sdio_mmc_card *) priv->phandle->card)->func); + if (ret) { + PRINTM(MERROR, "sdio_readb: reading register 0x%X failed\n", reg); + goto done; + } + param.cmd52_params[2] = data; + } else { + data = (t_u8) param.cmd52_params[2]; + PRINTM(MINFO, "Cmd52 write, func=%d, reg=0x%08X, data=0x%02X\n", func, + reg, data); + sdio_claim_host(((struct sdio_mmc_card *) priv->phandle->card)->func); + if (func) + sdio_writeb(((struct sdio_mmc_card *) priv->phandle->card)->func, + data, reg, &ret); + else + sdio_f0_writeb(((struct sdio_mmc_card *) priv->phandle->card)->func, + data, reg, &ret); + sdio_release_host(((struct sdio_mmc_card *) priv->phandle->card)->func); + if (ret) { + PRINTM(MERROR, "sdio_writeb: writing register 0x%X failed\n", reg); + goto done; + } + } + /* Copy to user */ + if (copy_to_user(req->ifr_data, ¶m, sizeof(param))) { + PRINTM(MERROR, "Copy to user failed!\n"); + ret = -EFAULT; + } + done: + LEAVE(); + return ret; +} + +/** + * @brief configure snmp mib + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int +woal_uap_snmp_mib(struct net_device *dev, struct ifreq *req) +{ + moal_private *priv = (moal_private *) netdev_priv(dev); + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_snmp_mib *snmp = NULL; + snmp_mib_para param; + t_u8 value[MAX_SNMP_VALUE_SIZE]; + int ret = 0; + + ENTER(); + memset(¶m, 0, sizeof(param)); + memset(value, 0, MAX_SNMP_VALUE_SIZE); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "woal_uap_snmp_mib() corrupt data\n"); + ret = -EFAULT; + goto done; + } + + /* Copy from user */ + if (copy_from_user(¶m, req->ifr_data, sizeof(param))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + DBG_HEXDUMP(MCMD_D, "snmp_mib_para", (t_u8 *) & param, sizeof(param)); + if (param.action) { + if (copy_from_user(value, req->ifr_data + sizeof(param), + MIN(param.oid_val_len, MAX_SNMP_VALUE_SIZE))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + DBG_HEXDUMP(MCMD_D, "snmp_mib_para value", value, + MIN(param.oid_val_len, sizeof(t_u32))); + } + + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_snmp_mib)); + if (ioctl_req == NULL) { + LEAVE(); + return -ENOMEM; + } + snmp = (mlan_ds_snmp_mib *) ioctl_req->pbuf; + ioctl_req->req_id = MLAN_IOCTL_SNMP_MIB; + switch (param.oid) { + case OID_80211D_ENABLE: + snmp->sub_command = MLAN_OID_SNMP_MIB_DOT11D; + break; + case OID_80211H_ENABLE: + snmp->sub_command = MLAN_OID_SNMP_MIB_DOT11H; + break; + default: + PRINTM(MERROR, "%s: Unsupported SNMP_MIB OID (%d).\n", __FUNCTION__, + param.oid); + goto done; + } + + if (!param.action) { + /* Get mib value from MLAN */ + ioctl_req->action = MLAN_ACT_GET; + } else { + /* Set mib value to MLAN */ + ioctl_req->action = MLAN_ACT_SET; + snmp->param.oid_value = *(t_u32 *) value; + } + if (MLAN_STATUS_SUCCESS != + woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto done; + } + + /* Copy to user */ + if (copy_to_user(req->ifr_data, ¶m, sizeof(param))) { + PRINTM(MERROR, "Copy to user failed!\n"); + ret = -EFAULT; + goto done; + } + if (!param.action) { /* GET */ + if (copy_to_user(req->ifr_data + sizeof(param), &snmp->param.oid_value, + MIN(param.oid_val_len, sizeof(t_u32)))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + } + + done: + if (ioctl_req) + kfree(ioctl_req); + LEAVE(); + return ret; +} + +/** + * @brief configure domain info + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int +woal_uap_domain_info(struct net_device *dev, struct ifreq *req) +{ + moal_private *priv = (moal_private *) netdev_priv(dev); + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_11d_cfg *cfg11d = NULL; + domain_info_para param; + t_u8 tlv[MAX_DOMAIN_TLV_LEN]; + t_u16 tlv_data_len = 0; + int ret = 0; + + ENTER(); + memset(¶m, 0, sizeof(param)); + memset(tlv, 0, MAX_DOMAIN_TLV_LEN); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "woal_uap_domain_info() corrupt data\n"); + ret = -EFAULT; + goto done; + } + + /* Copy from user */ + if (copy_from_user(¶m, req->ifr_data, sizeof(param))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + DBG_HEXDUMP(MCMD_D, "domain_info_para", (t_u8 *) & param, sizeof(param)); + if (param.action) { + /* get tlv header */ + if (copy_from_user(tlv, req->ifr_data + sizeof(param), TLV_HEADER_LEN)) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + tlv_data_len = ((t_u16 *) (tlv))[1]; + if ((TLV_HEADER_LEN + tlv_data_len) > sizeof(tlv)) { + PRINTM(MERROR, "TLV buffer is overflowed"); + ret = -EINVAL; + goto done; + } + /* get full tlv */ + if (copy_from_user(tlv, req->ifr_data + sizeof(param), + TLV_HEADER_LEN + tlv_data_len)) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + DBG_HEXDUMP(MCMD_D, "domain_info_para tlv", tlv, + TLV_HEADER_LEN + tlv_data_len); + } + + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11d_cfg)); + if (ioctl_req == NULL) { + LEAVE(); + return -ENOMEM; + } + cfg11d = (mlan_ds_11d_cfg *) ioctl_req->pbuf; + ioctl_req->req_id = MLAN_IOCTL_11D_CFG; + cfg11d->sub_command = MLAN_OID_11D_DOMAIN_INFO; + + if (!param.action) { + /* Get mib value from MLAN */ + ioctl_req->action = MLAN_ACT_GET; + } else { + /* Set mib value to MLAN */ + ioctl_req->action = MLAN_ACT_SET; + memcpy(cfg11d->param.domain_tlv, tlv, + MIN(MAX_IE_SIZE, (TLV_HEADER_LEN + tlv_data_len))); + } + if (MLAN_STATUS_SUCCESS != + woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto done; + } + + /* Copy to user */ + if (copy_to_user(req->ifr_data, ¶m, sizeof(param))) { + PRINTM(MERROR, "Copy to user failed!\n"); + ret = -EFAULT; + goto done; + } + if (!param.action) { /* GET */ + tlv_data_len = ((t_u16 *) (cfg11d->param.domain_tlv))[1]; + if (copy_to_user + (req->ifr_data + sizeof(param), &cfg11d->param.domain_tlv, + TLV_HEADER_LEN + tlv_data_len)) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + } + + done: + if (ioctl_req) + kfree(ioctl_req); + LEAVE(); + return ret; +} + +#if defined(DFS_TESTING_SUPPORT) +/** + * @brief configure dfs testing settings + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int +woal_uap_dfs_testing(struct net_device *dev, struct ifreq *req) +{ + moal_private *priv = (moal_private *) netdev_priv(dev); + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_11h_cfg *cfg11h = NULL; + dfs_testing_para param; + int ret = 0; + + ENTER(); + memset(¶m, 0, sizeof(param)); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "woal_uap_dfs_testing() corrupt data\n"); + ret = -EFAULT; + goto done; + } + + /* Copy from user */ + if (copy_from_user(¶m, req->ifr_data, sizeof(param))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + DBG_HEXDUMP(MCMD_D, "dfs_testing_para", (t_u8 *) & param, sizeof(param)); + + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11h_cfg)); + if (ioctl_req == NULL) { + LEAVE(); + return -ENOMEM; + } + cfg11h = (mlan_ds_11h_cfg *) ioctl_req->pbuf; + ioctl_req->req_id = MLAN_IOCTL_11H_CFG; + cfg11h->sub_command = MLAN_OID_11H_DFS_TESTING; + + if (!param.action) { + /* Get mib value from MLAN */ + ioctl_req->action = MLAN_ACT_GET; + } else { + /* Set mib value to MLAN */ + ioctl_req->action = MLAN_ACT_SET; + cfg11h->param.dfs_testing.usr_cac_period_msec = param.usr_cac_period; + cfg11h->param.dfs_testing.usr_nop_period_sec = param.usr_nop_period; + cfg11h->param.dfs_testing.usr_no_chan_change = param.no_chan_change; + cfg11h->param.dfs_testing.usr_fixed_new_chan = param.fixed_new_chan; + priv->phandle->cac_period_jiffies = param.usr_cac_period * HZ / 1000; + } + if (MLAN_STATUS_SUCCESS != + woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto done; + } + + if (!param.action) { /* GET */ + param.usr_cac_period = cfg11h->param.dfs_testing.usr_cac_period_msec; + param.usr_nop_period = cfg11h->param.dfs_testing.usr_nop_period_sec; + param.no_chan_change = cfg11h->param.dfs_testing.usr_no_chan_change; + param.fixed_new_chan = cfg11h->param.dfs_testing.usr_fixed_new_chan; + } + /* Copy to user */ + if (copy_to_user(req->ifr_data, ¶m, sizeof(param))) { + PRINTM(MERROR, "Copy to user failed!\n"); + ret = -EFAULT; + goto done; + } + + done: + if (ioctl_req) + kfree(ioctl_req); + LEAVE(); + return ret; +} +#endif + +/** + * @brief Configure TX beamforming support + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int +woal_uap_tx_bf_cfg(struct net_device *dev, struct ifreq *req) +{ + int ret = 0; + moal_private *priv = (moal_private *) netdev_priv(dev); + mlan_ds_11n_tx_bf_cfg bf_cfg; + tx_bf_cfg_para_hdr param; + t_u16 action = 0; + + ENTER(); + + memset(¶m, 0, sizeof(param)); + memset(&bf_cfg, 0, sizeof(bf_cfg)); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "woal_uap_tx_bf_cfg corrupt data\n"); + ret = -EFAULT; + goto done; + } + if (copy_from_user(¶m, req->ifr_data, sizeof(param))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + if (!param.action) { + /* Get BF configurations */ + action = MLAN_ACT_GET; + } else { + /* Set BF configurations */ + action = MLAN_ACT_SET; + } + if (copy_from_user(&bf_cfg, req->ifr_data + sizeof(tx_bf_cfg_para_hdr), + sizeof(bf_cfg))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + DBG_HEXDUMP(MCMD_D, "bf_cfg", (t_u8 *) & bf_cfg, sizeof(bf_cfg)); + + if (MLAN_STATUS_SUCCESS != woal_set_get_tx_bf_cfg(priv, action, &bf_cfg)) { + ret = -EFAULT; + goto done; + } + + /* Copy to user */ + if (copy_to_user(req->ifr_data + sizeof(tx_bf_cfg_para_hdr), + &bf_cfg, sizeof(bf_cfg))) { + PRINTM(MERROR, "Copy to user failed!\n"); + ret = -EFAULT; + goto done; + } + + done: + LEAVE(); + return ret; +} + +/** + * @brief uap hs_cfg ioctl handler + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int +woal_uap_hs_cfg(struct net_device *dev, struct ifreq *req, + BOOLEAN invoke_hostcmd) +{ + moal_private *priv = (moal_private *) netdev_priv(dev); + mlan_ds_hs_cfg hscfg; + ds_hs_cfg hs_cfg; + mlan_bss_info bss_info; + t_u16 action; + int ret = 0; + + ENTER(); + + memset(&hscfg, 0, sizeof(mlan_ds_hs_cfg)); + memset(&hs_cfg, 0, sizeof(ds_hs_cfg)); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "uap_hs_cfg() corrupt data\n"); + ret = -EFAULT; + goto done; + } + if (copy_from_user(&hs_cfg, req->ifr_data, sizeof(ds_hs_cfg))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + + PRINTM(MIOCTL, "ioctl hscfg: flags=0x%x condition=0x%x gpio=%d gap=0x%x\n", + hs_cfg.flags, hs_cfg.conditions, (int) hs_cfg.gpio, hs_cfg.gap); + + /* HS config is blocked if HS is already activated */ + if ((hs_cfg.flags & HS_CFG_FLAG_CONDITION) && + (hs_cfg.conditions != HOST_SLEEP_CFG_CANCEL || + invoke_hostcmd == MFALSE)) { + memset(&bss_info, 0, sizeof(bss_info)); + woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info); + if (bss_info.is_hs_configured) { + PRINTM(MERROR, "HS already configured\n"); + ret = -EFAULT; + goto done; + } + } + + if (hs_cfg.flags & HS_CFG_FLAG_SET) { + action = MLAN_ACT_SET; + if (hs_cfg.flags != HS_CFG_FLAG_ALL) { + woal_set_get_hs_params(priv, MLAN_ACT_GET, MOAL_IOCTL_WAIT, &hscfg); + } + if (hs_cfg.flags & HS_CFG_FLAG_CONDITION) + hscfg.conditions = hs_cfg.conditions; + if (hs_cfg.flags & HS_CFG_FLAG_GPIO) + hscfg.gpio = hs_cfg.gpio; + if (hs_cfg.flags & HS_CFG_FLAG_GAP) + hscfg.gap = hs_cfg.gap; + + if (invoke_hostcmd == MTRUE) { + /* Issue IOCTL to set up parameters */ + hscfg.is_invoke_hostcmd = MFALSE; + if (MLAN_STATUS_SUCCESS != + woal_set_get_hs_params(priv, action, MOAL_IOCTL_WAIT, &hscfg)) { + ret = -EFAULT; + goto done; + } + } + } else { + action = MLAN_ACT_GET; + } + + /* Issue IOCTL to invoke hostcmd */ + hscfg.is_invoke_hostcmd = invoke_hostcmd; + if (MLAN_STATUS_SUCCESS != + woal_set_get_hs_params(priv, action, MOAL_IOCTL_WAIT, &hscfg)) { + ret = -EFAULT; + goto done; + } + if (!(hs_cfg.flags & HS_CFG_FLAG_SET)) { + hs_cfg.flags = + HS_CFG_FLAG_CONDITION | HS_CFG_FLAG_GPIO | HS_CFG_FLAG_GAP; + hs_cfg.conditions = hscfg.conditions; + hs_cfg.gpio = hscfg.gpio; + hs_cfg.gap = hscfg.gap; + /* Copy to user */ + if (copy_to_user(req->ifr_data, &hs_cfg, sizeof(ds_hs_cfg))) { + PRINTM(MERROR, "Copy to user failed!\n"); + ret = -EFAULT; + goto done; + } + } + + done: + LEAVE(); + return ret; +} + +/** + * @brief Set Host Sleep parameters + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int +woal_uap_hs_set_para(struct net_device *dev, struct ifreq *req) +{ + int ret = 0; + + ENTER(); + + if (req->ifr_data != NULL) { + ret = woal_uap_hs_cfg(dev, req, MFALSE); + goto done; + } else { + PRINTM(MERROR, "Invalid data\n"); + ret = -EINVAL; + goto done; + } + done: + LEAVE(); + return ret; +} + +/** + * @brief uap mgmt_frame_control ioctl handler + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int +woal_uap_mgmt_frame_control(struct net_device *dev, struct ifreq *req) +{ + moal_private *priv = (moal_private *) netdev_priv(dev); + int ret = 0; + t_u16 action = 0; + mgmt_frame_ctrl param; + mlan_uap_bss_param sys_config; + + ENTER(); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "uap_mgmt_frame_ctrl() corrupt data\n"); + ret = -EFAULT; + goto done; + } + + /* Get user data */ + if (copy_from_user(¶m, req->ifr_data, sizeof(param))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + if (param.action) { + action = MLAN_ACT_SET; + } else { + action = MLAN_ACT_GET; + } + if (action == MLAN_ACT_SET) { + /* Initialize the invalid values so that the correct values below are + downloaded to firmware */ + woal_set_sys_config_invalid_data(&sys_config); + sys_config.mgmt_ie_passthru_mask = param.mask; + } + + if (MLAN_STATUS_SUCCESS != + woal_set_get_sys_config(priv, action, MOAL_IOCTL_WAIT, &sys_config)) { + ret = -EFAULT; + goto done; + } + + if (action == MLAN_ACT_GET) { + param.mask = sys_config.mgmt_ie_passthru_mask; + if (copy_to_user(req->ifr_data, ¶m, sizeof(param))) { + PRINTM(MERROR, "Copy to user failed\n"); + ret = -EFAULT; + } + } + done: + LEAVE(); + return ret; +} + +/** + * @brief Set/Get tx rate + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * + * @return 0 --success, otherwise fail + */ +static int +woal_uap_tx_rate_cfg(struct net_device *dev, struct ifreq *req) +{ + moal_private *priv = (moal_private *) netdev_priv(dev); + int ret = 0, i = 0; + mlan_ds_rate *rate = NULL; + mlan_ioctl_req *mreq = NULL; + tx_rate_cfg_t tx_rate_config; + + ENTER(); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "uap_tx_rate_cfg() corrupt data\n"); + ret = -EFAULT; + goto done; + } + + memset(&tx_rate_config, 0, sizeof(tx_rate_cfg_t)); + /* Get user data */ + if (copy_from_user(&tx_rate_config, req->ifr_data, sizeof(tx_rate_cfg_t))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + + mreq = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_rate)); + if (mreq == NULL) { + ret = -ENOMEM; + goto done; + } + rate = (mlan_ds_rate *) mreq->pbuf; + rate->param.rate_cfg.rate_type = MLAN_RATE_INDEX; + rate->sub_command = MLAN_OID_RATE_CFG; + mreq->req_id = MLAN_IOCTL_RATE; + if (!(tx_rate_config.action)) + mreq->action = MLAN_ACT_GET; + else { + mreq->action = MLAN_ACT_SET; + if (tx_rate_config.rate == AUTO_RATE) + rate->param.rate_cfg.is_rate_auto = 1; + else { + if ((tx_rate_config.rate != MLAN_RATE_INDEX_MCS32) && + ((tx_rate_config.rate < 0) || + (tx_rate_config.rate > MLAN_RATE_INDEX_MCS15))) { + ret = -EINVAL; + goto done; + } + } + rate->param.rate_cfg.rate = tx_rate_config.rate; + } + + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, mreq, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto done; + } + if (tx_rate_config.action) { + priv->rate_index = tx_rate_config.action; + } else { + if (rate->param.rate_cfg.is_rate_auto) + tx_rate_config.rate = AUTO_RATE; + else + tx_rate_config.rate = rate->param.rate_cfg.rate; + for (i = 0; i < MAX_BITMAP_RATES_SIZE; i++) { + tx_rate_config.bitmap_rates[i] = + rate->param.rate_cfg.bitmap_rates[i]; + } + + if (copy_to_user(req->ifr_data, &tx_rate_config, sizeof(tx_rate_cfg_t))) { + PRINTM(MERROR, "Copy to user failed\n"); + ret = -EFAULT; + } + } + done: + if (mreq) + kfree(mreq); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get RF antenna mode + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * + * @return 0 --success, otherwise fail + */ +static int +woal_uap_antenna_cfg(struct net_device *dev, struct ifreq *req) +{ + moal_private *priv = (moal_private *) netdev_priv(dev); + int ret = 0; + mlan_ds_radio_cfg *radio = NULL; + mlan_ioctl_req *mreq = NULL; + ant_cfg_t antenna_config; + + ENTER(); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "uap_antenna_cfg() corrupt data\n"); + ret = -EFAULT; + goto done; + } + + memset(&antenna_config, 0, sizeof(ant_cfg_t)); + /* Get user data */ + if (copy_from_user(&antenna_config, req->ifr_data, sizeof(ant_cfg_t))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + mreq = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_radio_cfg)); + if (mreq == NULL) { + ret = -ENOMEM; + goto done; + } + radio = (mlan_ds_radio_cfg *) mreq->pbuf; + radio->sub_command = MLAN_OID_ANT_CFG; + mreq->req_id = MLAN_IOCTL_RADIO_CFG; + if (!(antenna_config.action)) + mreq->action = MLAN_ACT_GET; + else { + mreq->action = MLAN_ACT_SET; + radio->param.ant_cfg.tx_antenna = antenna_config.tx_mode; + radio->param.ant_cfg.rx_antenna = antenna_config.rx_mode; + } + + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, mreq, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto done; + } + if (mreq->action == MLAN_ACT_GET) { + antenna_config.tx_mode = radio->param.ant_cfg.tx_antenna; + antenna_config.rx_mode = radio->param.ant_cfg.rx_antenna; + if (copy_to_user(req->ifr_data, &antenna_config, sizeof(ant_cfg_t))) { + PRINTM(MERROR, "Copy to user failed\n"); + ret = -EFAULT; + } + } + done: + if (mreq) + kfree(mreq); + LEAVE(); + return ret; +} + +/** + * @brief uap ioctl handler + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int +woal_uap_ioctl(struct net_device *dev, struct ifreq *req) +{ + int ret = 0; + t_u32 subcmd = 0; + ENTER(); + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "uap_ioctl() corrupt data\n"); + ret = -EFAULT; + goto done; + } + if (copy_from_user(&subcmd, req->ifr_data, sizeof(subcmd))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + + PRINTM(MIOCTL, "ioctl subcmd=%d\n", (int) subcmd); + switch (subcmd) { + case UAP_ADDBA_PARA: + ret = woal_uap_addba_param(dev, req); + break; + case UAP_AGGR_PRIOTBL: + ret = woal_uap_aggr_priotbl(dev, req); + break; + case UAP_ADDBA_REJECT: + ret = woal_uap_addba_reject(dev, req); + break; + case UAP_FW_INFO: + ret = woal_uap_get_fw_info(dev, req); + break; + case UAP_DEEP_SLEEP: + ret = woal_uap_deep_sleep(dev, req); + break; + case UAP_TX_DATA_PAUSE: + ret = woal_uap_txdatapause(dev, req); + break; + case UAP_SDCMD52_RW: + ret = woal_uap_sdcmd52_rw(dev, req); + break; + case UAP_SNMP_MIB: + ret = woal_uap_snmp_mib(dev, req); + break; + case UAP_DOMAIN_INFO: + ret = woal_uap_domain_info(dev, req); + break; +#ifdef DFS_TESTING_SUPPORT + case UAP_DFS_TESTING: + ret = woal_uap_dfs_testing(dev, req); + break; +#endif + case UAP_TX_BF_CFG: + ret = woal_uap_tx_bf_cfg(dev, req); + break; + case UAP_HS_CFG: + ret = woal_uap_hs_cfg(dev, req, MTRUE); + break; + case UAP_HS_SET_PARA: + ret = woal_uap_hs_set_para(dev, req); + break; + case UAP_MGMT_FRAME_CONTROL: + ret = woal_uap_mgmt_frame_control(dev, req); + break; + case UAP_TX_RATE_CFG: + ret = woal_uap_tx_rate_cfg(dev, req); + break; + case UAP_ANTENNA_CFG: + ret = woal_uap_antenna_cfg(dev, req); + break; + default: + break; + } + done: + LEAVE(); + return ret; +} + +/** + * @brief uap station deauth ioctl handler + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int +woal_uap_sta_deauth_ioctl(struct net_device *dev, struct ifreq *req) +{ + moal_private *priv = (moal_private *) netdev_priv(dev); + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_bss *bss = NULL; + mlan_deauth_param deauth_param; + int ret = 0; + + ENTER(); + + memset(&deauth_param, 0, sizeof(mlan_deauth_param)); + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "uap_sta_deauth_ioctl() corrupt data\n"); + ret = -EFAULT; + goto done; + } + if (copy_from_user(&deauth_param, req->ifr_data, sizeof(mlan_deauth_param))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + + PRINTM(MIOCTL, + "ioctl deauth station: %02x:%02x:%02x:%02x:%02x:%02x, reason=%d\n", + deauth_param.mac_addr[0], deauth_param.mac_addr[1], + deauth_param.mac_addr[2], deauth_param.mac_addr[3], + deauth_param.mac_addr[4], deauth_param.mac_addr[5], + deauth_param.reason_code); + + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (ioctl_req == NULL) { + ret = -ENOMEM; + goto done; + } + bss = (mlan_ds_bss *) ioctl_req->pbuf; + + bss->sub_command = MLAN_OID_UAP_DEAUTH_STA; + ioctl_req->req_id = MLAN_IOCTL_BSS; + ioctl_req->action = MLAN_ACT_SET; + + memcpy(&bss->param.deauth_param, &deauth_param, sizeof(mlan_deauth_param)); + if (MLAN_STATUS_SUCCESS != + woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + if (copy_to_user(req->ifr_data, &ioctl_req->status_code, sizeof(t_u32))) + PRINTM(MERROR, "Copy to user failed!\n"); + goto done; + } + + done: + if (ioctl_req) + kfree(ioctl_req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get radio + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * + * @return 0 --success, otherwise fail + */ +static int +woal_uap_radio_ctl(struct net_device *dev, struct ifreq *req) +{ + moal_private *priv = (moal_private *) netdev_priv(dev); + int ret = 0; + mlan_ds_radio_cfg *radio = NULL; + mlan_ioctl_req *mreq = NULL; + int data[2] = { 0, 0 }; + mlan_bss_info bss_info; + + ENTER(); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "uap_radio_ctl() corrupt data\n"); + ret = -EFAULT; + goto done; + } + + /* Get user data */ + if (copy_from_user(&data, req->ifr_data, sizeof(data))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + + if (data[0]) { + mreq = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_radio_cfg)); + if (mreq == NULL) { + ret = -ENOMEM; + goto done; + } + radio = (mlan_ds_radio_cfg *) mreq->pbuf; + radio->sub_command = MLAN_OID_RADIO_CTRL; + mreq->req_id = MLAN_IOCTL_RADIO_CFG; + mreq->action = MLAN_ACT_SET; + radio->param.radio_on_off = (t_u32) data[1]; + if (MLAN_STATUS_SUCCESS != + woal_request_ioctl(priv, mreq, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + } + if (mreq) + kfree(mreq); + } else { + /* Get radio status */ + memset(&bss_info, 0, sizeof(bss_info)); + woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info); + + data[1] = bss_info.radio_on; + if (copy_to_user(req->ifr_data, data, sizeof(data))) { + PRINTM(MERROR, "Copy to user failed\n"); + ret = -EFAULT; + } + } + done: + LEAVE(); + return ret; +} + +/** + * @brief uap bss control ioctl handler + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int +woal_uap_bss_ctrl_ioctl(struct net_device *dev, struct ifreq *req) +{ + moal_private *priv = (moal_private *) netdev_priv(dev); + int ret = 0, data = 0; + + ENTER(); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "uap_bss_ctrl() corrupt data\n"); + ret = -EFAULT; + goto done; + } + if (copy_from_user(&data, req->ifr_data, sizeof(data))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + + ret = woal_uap_bss_ctrl(priv, MOAL_IOCTL_WAIT, data); + + done: + LEAVE(); + return ret; +} + +/** + * @brief uap power mode ioctl handler + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int +woal_uap_power_mode_ioctl(struct net_device *dev, struct ifreq *req) +{ + moal_private *priv = (moal_private *) netdev_priv(dev); + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_pm_cfg *pm_cfg = NULL; + mlan_ds_ps_mgmt ps_mgmt; + int ret = 0; + + ENTER(); + + memset(&ps_mgmt, 0, sizeof(mlan_ds_ps_mgmt)); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "uap_power_mode_ioctl() corrupt data\n"); + ret = -EFAULT; + goto done; + } + if (copy_from_user(&ps_mgmt, req->ifr_data, sizeof(mlan_ds_ps_mgmt))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + + PRINTM(MIOCTL, + "ioctl power: flag=0x%x ps_mode=%d ctrl_bitmap=%d min_sleep=%d max_sleep=%d " + "inact_to=%d min_awake=%d max_awake=%d\n", ps_mgmt.flags, + (int) ps_mgmt.ps_mode, (int) ps_mgmt.sleep_param.ctrl_bitmap, + (int) ps_mgmt.sleep_param.min_sleep, + (int) ps_mgmt.sleep_param.max_sleep, + (int) ps_mgmt.inact_param.inactivity_to, + (int) ps_mgmt.inact_param.min_awake, + (int) ps_mgmt.inact_param.max_awake); + + if (ps_mgmt. + flags & ~(PS_FLAG_PS_MODE | PS_FLAG_SLEEP_PARAM | + PS_FLAG_INACT_SLEEP_PARAM)) { + PRINTM(MERROR, "Invalid parameter: flags = 0x%x\n", ps_mgmt.flags); + ret = -EINVAL; + goto done; + } + + if (ps_mgmt.ps_mode > PS_MODE_INACTIVITY) { + PRINTM(MERROR, "Invalid parameter: ps_mode = %d\n", + (int) ps_mgmt.flags); + ret = -EINVAL; + goto done; + } + + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_pm_cfg)); + if (ioctl_req == NULL) { + ret = -ENOMEM; + goto done; + } + pm_cfg = (mlan_ds_pm_cfg *) ioctl_req->pbuf; + pm_cfg->sub_command = MLAN_OID_PM_CFG_PS_MODE; + ioctl_req->req_id = MLAN_IOCTL_PM_CFG; + if (ps_mgmt.flags) { + ioctl_req->action = MLAN_ACT_SET; + memcpy(&pm_cfg->param.ps_mgmt, &ps_mgmt, sizeof(mlan_ds_ps_mgmt)); + } else { + ioctl_req->action = MLAN_ACT_GET; + } + + if (MLAN_STATUS_SUCCESS != + woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + if (copy_to_user(req->ifr_data, &ioctl_req->status_code, sizeof(t_u32))) + PRINTM(MERROR, "Copy to user failed!\n"); + goto done; + } + if (!ps_mgmt.flags) { + /* Copy to user */ + if (copy_to_user + (req->ifr_data, &pm_cfg->param.ps_mgmt, sizeof(mlan_ds_ps_mgmt))) { + PRINTM(MERROR, "Copy to user failed!\n"); + ret = -EFAULT; + goto done; + } + } + done: + if (ioctl_req) + kfree(ioctl_req); + LEAVE(); + return ret; +} + +/** + * @brief uap BSS config ioctl handler + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int +woal_uap_bss_cfg_ioctl(struct net_device *dev, struct ifreq *req) +{ + moal_private *priv = (moal_private *) netdev_priv(dev); + int ret = 0; + mlan_ds_bss *bss = NULL; + mlan_ioctl_req *ioctl_req = NULL; + int offset = 0; + t_u32 action = 0; + + ENTER(); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "uap_bss_cfg_ioctl() corrupt data\n"); + ret = -EFAULT; + goto done; + } + + /* Get action */ + if (copy_from_user(&action, req->ifr_data + offset, sizeof(action))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + offset += sizeof(action); + + /* Allocate an IOCTL request buffer */ + ioctl_req = + (mlan_ioctl_req *) woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (ioctl_req == NULL) { + ret = -ENOMEM; + goto done; + } + + bss = (mlan_ds_bss *) ioctl_req->pbuf; + bss->sub_command = MLAN_OID_UAP_BSS_CONFIG; + ioctl_req->req_id = MLAN_IOCTL_BSS; + if (action == 1) { + ioctl_req->action = MLAN_ACT_SET; + } else { + ioctl_req->action = MLAN_ACT_GET; + } + + if (ioctl_req->action == MLAN_ACT_SET) { + /* Get the BSS config from user */ + if (copy_from_user + (&bss->param.bss_config, req->ifr_data + offset, + sizeof(mlan_uap_bss_param))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + } + + if (MLAN_STATUS_SUCCESS != + woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto done; + } + + if (ioctl_req->action == MLAN_ACT_GET) { + offset = sizeof(action); + + /* Copy to user : BSS config */ + if (copy_to_user + (req->ifr_data + offset, &bss->param.bss_config, + sizeof(mlan_uap_bss_param))) { + PRINTM(MERROR, "Copy to user failed!\n"); + ret = -EFAULT; + goto done; + } + } + done: + if (ioctl_req) + kfree(ioctl_req); + LEAVE(); + return ret; +} + +/** + * @brief uap get station list handler + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int +woal_uap_get_sta_list_ioctl(struct net_device *dev, struct ifreq *req) +{ + moal_private *priv = (moal_private *) netdev_priv(dev); + int ret = 0; + mlan_ds_get_info *info = NULL; + mlan_ioctl_req *ioctl_req = NULL; + + ENTER(); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "uap_get_sta_list_ioctl() corrupt data\n"); + ret = -EFAULT; + goto done; + } + + /* Allocate an IOCTL request buffer */ + ioctl_req = + (mlan_ioctl_req *) woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_get_info)); + if (ioctl_req == NULL) { + ret = -ENOMEM; + goto done; + } + + info = (mlan_ds_get_info *) ioctl_req->pbuf; + info->sub_command = MLAN_OID_UAP_STA_LIST; + ioctl_req->req_id = MLAN_IOCTL_GET_INFO; + ioctl_req->action = MLAN_ACT_GET; + + if (MLAN_STATUS_SUCCESS != + woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto done; + } + + if (ioctl_req->action == MLAN_ACT_GET) { + /* Copy to user : sta_list */ + if (copy_to_user + (req->ifr_data, &info->param.sta_list, sizeof(mlan_ds_sta_list))) { + PRINTM(MERROR, "Copy to user failed!\n"); + ret = -EFAULT; + goto done; + } + } + done: + if (ioctl_req) + kfree(ioctl_req); + LEAVE(); + return ret; +} + +/** + * @brief uAP set WAPI key ioctl + * + * @param priv A pointer to moal_private structure + * @param msg A pointer to wapi_msg structure + * + * @return 0 --success, otherwise fail + */ +static int +woal_uap_set_wapi_key_ioctl(moal_private * priv, wapi_msg * msg) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_sec_cfg *sec = NULL; + int ret = 0; + wapi_key_msg *key_msg = NULL; + t_u8 bcast_addr[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + + ENTER(); + if (msg->msg_len != sizeof(wapi_key_msg)) { + ret = -EINVAL; + goto done; + } + key_msg = (wapi_key_msg *) msg->msg; + + 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.is_wapi_key = MTRUE; + sec->param.encrypt_key.key_len = MLAN_MAX_KEY_LENGTH; + memcpy(sec->param.encrypt_key.mac_addr, key_msg->mac_addr, ETH_ALEN); + sec->param.encrypt_key.key_index = key_msg->key_id; + if (0 == memcmp(key_msg->mac_addr, bcast_addr, ETH_ALEN)) + sec->param.encrypt_key.key_flags = KEY_FLAG_GROUP_KEY; + else + sec->param.encrypt_key.key_flags = KEY_FLAG_SET_TX_KEY; + memcpy(sec->param.encrypt_key.key_material, key_msg->key, + sec->param.encrypt_key.key_len); + + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) + ret = -EFAULT; + done: + if (req) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Enable/Disable wapi in firmware + * + * @param priv A pointer to moal_private structure + * @param enable MTRUE/MFALSE + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail + */ +static mlan_status +woal_enable_wapi(moal_private * priv, t_u8 enable) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_bss *bss = NULL; + mlan_status status; + + ENTER(); + + /* Allocate an IOCTL request buffer */ + req = (mlan_ioctl_req *) woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (req == NULL) { + status = MLAN_STATUS_FAILURE; + goto done; + } + + /* Fill request buffer */ + bss = (mlan_ds_bss *) req->pbuf; + bss->sub_command = MLAN_OID_UAP_BSS_CONFIG; + req->req_id = MLAN_IOCTL_BSS; + req->action = MLAN_ACT_GET; + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "Get AP setting failed! status=%d, error_code=0x%x\n", + status, req->status_code); + } + + /* Change AP default setting */ + req->action = MLAN_ACT_SET; + if (enable == MFALSE) { + bss->param.bss_config.auth_mode = MLAN_AUTH_MODE_OPEN; + bss->param.bss_config.protocol = PROTOCOL_NO_SECURITY; + } else { + bss->param.bss_config.auth_mode = MLAN_AUTH_MODE_OPEN; + bss->param.bss_config.protocol = PROTOCOL_WAPI; + } + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "Set AP setting failed! status=%d, error_code=0x%x\n", + status, req->status_code); + } + if (enable) + woal_uap_bss_ctrl(priv, MOAL_IOCTL_WAIT, UAP_BSS_START); + done: + if (req) + kfree(req); + LEAVE(); + return status; +} + +/** + * @brief uAP set WAPI flag ioctl + * + * @param priv A pointer to moal_private structure + * @param msg A pointer to wapi_msg structure + * + * @return 0 --success, otherwise fail + */ +static int +woal_uap_set_wapi_flag_ioctl(moal_private * priv, wapi_msg * msg) +{ + t_u8 wapi_psk_ie[] = + { 0x44, 0x14, 0x01, 0x00, 0x01, 0x00, 0x00, 0x14, 0x72, 0x02, + 0x01, 0x00, 0x00, 0x14, 0x72, 0x01, 0x00, 0x14, 0x72, 0x01, + 0x00, 0x00 + }; + t_u8 wapi_cert_ie[] = + { 0x44, 0x14, 0x01, 0x00, 0x01, 0x00, 0x00, 0x14, 0x72, 0x01, + 0x01, 0x00, 0x00, 0x14, 0x72, 0x01, 0x00, 0x14, 0x72, 0x01, + 0x00, 0x00 + }; + mlan_ds_misc_cfg *misc = NULL; + mlan_ioctl_req *req = NULL; + int ret = 0; + + ENTER(); + + woal_uap_bss_ctrl(priv, MOAL_IOCTL_WAIT, UAP_BSS_STOP); + + 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; + misc->sub_command = MLAN_OID_MISC_GEN_IE; + req->req_id = MLAN_IOCTL_MISC_CFG; + req->action = MLAN_ACT_SET; + + misc->param.gen_ie.type = MLAN_IE_TYPE_GEN_IE; + misc->param.gen_ie.len = sizeof(wapi_psk_ie); + if (msg->msg[0] & WAPI_MODE_PSK) { + memcpy(misc->param.gen_ie.ie_data, wapi_psk_ie, misc->param.gen_ie.len); + } else if (msg->msg[0] & WAPI_MODE_CERT) { + memcpy(misc->param.gen_ie.ie_data, wapi_cert_ie, + misc->param.gen_ie.len); + } else if (msg->msg[0] == 0) { + /* disable WAPI in driver */ + if (MLAN_STATUS_SUCCESS != + woal_set_wapi_enable(priv, MOAL_IOCTL_WAIT, 0)) + ret = -EFAULT; + woal_enable_wapi(priv, MFALSE); + goto done; + } else { + ret = -EINVAL; + goto done; + } + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto done; + } + woal_enable_wapi(priv, MTRUE); + done: + if (req) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief set wapi ioctl function + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int +woal_uap_set_wapi(struct net_device *dev, struct ifreq *req) +{ + moal_private *priv = (moal_private *) netdev_priv(dev); + wapi_msg msg; + int ret = 0; + + ENTER(); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "uap_set_wapi() corrupt data\n"); + ret = -EFAULT; + goto done; + } + + memset(&msg, 0, sizeof(msg)); + + if (copy_from_user(&msg, req->ifr_data, sizeof(msg))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + + PRINTM(MIOCTL, "set wapi msg_type = %d, msg_len=%d\n", msg.msg_type, + msg.msg_len); + DBG_HEXDUMP(MCMD_D, "wapi msg", msg.msg, MIN(msg.msg_len, sizeof(msg.msg))); + + switch (msg.msg_type) { + case P80211_PACKET_WAPIFLAG: + ret = woal_uap_set_wapi_flag_ioctl(priv, &msg); + break; + case P80211_PACKET_SETKEY: + ret = woal_uap_set_wapi_key_ioctl(priv, &msg); + break; + default: + ret = -EOPNOTSUPP; + break; + } + done: + LEAVE(); + return ret; +} + +/******************************************************** + Global Functions +********************************************************/ +/** + * @brief Initialize the members of mlan_uap_bss_param + * which are uploaded from firmware + * + * @param priv A pointer to moal_private structure + * @param sys_cfg A pointer to mlan_uap_bss_param structure + * @param wait_option Wait option + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +woal_uap_get_bss_param(moal_private * priv, mlan_uap_bss_param * sys_cfg, + t_u8 wait_option) +{ + mlan_ds_bss *info = NULL; + mlan_ioctl_req *req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (req == NULL) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + info = (mlan_ds_bss *) req->pbuf; + info->sub_command = MLAN_OID_UAP_BSS_CONFIG; + req->req_id = MLAN_IOCTL_BSS; + req->action = MLAN_ACT_GET; + + status = woal_request_ioctl(priv, req, wait_option); + if (status != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "Get bss info failed!\n"); + status = MLAN_STATUS_FAILURE; + goto done; + } + memcpy(sys_cfg, &info->param.bss_config, sizeof(mlan_uap_bss_param)); + + done: + if (req && (status != MLAN_STATUS_PENDING)) + kfree(req); + + LEAVE(); + return status; +} + +/** + * @brief Set 11n status based on the configured security mode + * + * @param sys_cfg A pointer to mlan_uap_bss_param structure + * @param action MLAN_ACT_DISABLE or MLAN_ACT_ENABLE + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +woal_uap_set_11n_status(mlan_uap_bss_param * sys_cfg, t_u8 action) +{ + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + if (action == MLAN_ACT_DISABLE) { + if ((sys_cfg->supported_mcs_set[0] == 0) + && (sys_cfg->supported_mcs_set[4] == 0) + && (sys_cfg->supported_mcs_set[1] == 0) + ) { + goto done; + } else { + sys_cfg->supported_mcs_set[0] = 0; + sys_cfg->supported_mcs_set[4] = 0; + sys_cfg->supported_mcs_set[1] = 0; + } + } + + if (action == MLAN_ACT_ENABLE) { + if ((sys_cfg->supported_mcs_set[0] != 0) + || (sys_cfg->supported_mcs_set[4] != 0) + || (sys_cfg->supported_mcs_set[1] != 0) + ) { + goto done; + } else { + sys_cfg->supported_mcs_set[0] = 0xFF; + sys_cfg->supported_mcs_set[4] = 0x01; + sys_cfg->supported_mcs_set[1] = 0xFF; + } + } + + done: + LEAVE(); + return status; +} + +/** + * @brief Parse AP configuration from ASCII string + * + * @param ap_cfg A pointer to mlan_uap_bss_param structure + * @param buf A pointer to user data + * + * @return 0 --success, otherwise fail + */ +int +woal_uap_ap_cfg_parse_data(mlan_uap_bss_param * ap_cfg, t_s8 * buf) +{ + int ret = 0, atoi_ret; + int set_sec = 0, set_key = 0, set_chan = 0; + int set_preamble = 0, set_scb = 0, set_ssid = 0; + t_s8 *begin = buf, *value = NULL, *opt = NULL; + + ENTER(); + + while (begin) { + value = woal_strsep(&begin, ',', '/'); + opt = woal_strsep(&value, '=', '/'); + if (opt && !strncmp(opt, "END", strlen("END"))) { + if (!ap_cfg->ssid.ssid_len) { + PRINTM(MERROR, "Minimum option required is SSID\n"); + ret = -EINVAL; + goto done; + } + PRINTM(MINFO, "Parsing terminated by string END\n"); + break; + } + if (!opt || !value || !value[0]) { + PRINTM(MERROR, "Invalid option\n"); + ret = -EINVAL; + goto done; + } else if (!strncmp(opt, "ASCII_CMD", strlen("ASCII_CMD"))) { + if (strncmp(value, "AP_CFG", strlen("AP_CFG"))) { + PRINTM(MERROR, "ASCII_CMD: %s not matched with AP_CFG\n", + value); + ret = -EFAULT; + goto done; + } + value = woal_strsep(&begin, ',', '/'); + opt = woal_strsep(&value, '=', '/'); + if (!opt || !value || !value[0]) { + PRINTM(MERROR, "Minimum option required is SSID\n"); + ret = -EINVAL; + goto done; + } else if (!strncmp(opt, "SSID", strlen("SSID"))) { + if (set_ssid) { + PRINTM(MWARN, "Skipping SSID, found again!\n"); + continue; + } + if (strlen(value) > MLAN_MAX_SSID_LENGTH) { + PRINTM(MERROR, "SSID length exceeds max length\n"); + ret = -EFAULT; + goto done; + } + ap_cfg->ssid.ssid_len = strlen(value); + strncpy((char *) ap_cfg->ssid.ssid, value, strlen(value)); + PRINTM(MINFO, "ssid=%s, len=%d\n", ap_cfg->ssid.ssid, + (int) ap_cfg->ssid.ssid_len); + set_ssid = 1; + } else { + PRINTM(MERROR, "AP_CFG: Invalid option %s, " + "expect SSID\n", opt); + ret = -EINVAL; + goto done; + } + } else if (!strncmp(opt, "SEC", strlen("SEC"))) { + if (set_sec) { + PRINTM(MWARN, "Skipping SEC, found again!\n"); + continue; + } + if (!strnicmp(value, "open", strlen("open"))) { + ap_cfg->auth_mode = MLAN_AUTH_MODE_OPEN; + if (set_key) + ap_cfg->wpa_cfg.length = 0; + ap_cfg->key_mgmt = KEY_MGMT_NONE; + ap_cfg->protocol = PROTOCOL_NO_SECURITY; + } else if (!strnicmp(value, "wpa2-psk", strlen("wpa2-psk"))) { + ap_cfg->auth_mode = MLAN_AUTH_MODE_OPEN; + ap_cfg->protocol = PROTOCOL_WPA2; + ap_cfg->key_mgmt = KEY_MGMT_PSK; + 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; + } else if (!strnicmp(value, "wpa-psk", strlen("wpa-psk"))) { + ap_cfg->auth_mode = MLAN_AUTH_MODE_OPEN; + ap_cfg->protocol = PROTOCOL_WPA; + ap_cfg->key_mgmt = KEY_MGMT_PSK; + ap_cfg->wpa_cfg.pairwise_cipher_wpa = CIPHER_TKIP; + ap_cfg->wpa_cfg.group_cipher = CIPHER_TKIP; + } else if (!strnicmp(value, "wep128", strlen("wep128"))) { + ap_cfg->auth_mode = MLAN_AUTH_MODE_OPEN; + if (set_key) + ap_cfg->wpa_cfg.length = 0; + ap_cfg->key_mgmt = KEY_MGMT_NONE; + ap_cfg->protocol = PROTOCOL_STATIC_WEP; + } else { + PRINTM(MERROR, "AP_CFG: Invalid value=%s for %s\n", value, opt); + ret = -EFAULT; + goto done; + } + set_sec = 1; + } else if (!strncmp(opt, "KEY", strlen("KEY"))) { + if (set_key) { + PRINTM(MWARN, "Skipping KEY, found again!\n"); + continue; + } + if (set_sec && ap_cfg->protocol == PROTOCOL_STATIC_WEP) { + if (strlen(value) != MAX_WEP_KEY_SIZE) { + PRINTM(MERROR, "Invalid WEP KEY length\n"); + ret = -EFAULT; + goto done; + } + ap_cfg->wep_cfg.key0.key_index = 0; + ap_cfg->wep_cfg.key0.is_default = 1; + ap_cfg->wep_cfg.key0.length = strlen(value); + memcpy(ap_cfg->wep_cfg.key0.key, value, strlen(value)); + set_key = 1; + continue; + } + if (set_sec && ap_cfg->protocol != PROTOCOL_WPA2 + && ap_cfg->protocol != PROTOCOL_WPA) { + PRINTM(MWARN, "Warning! No KEY for open mode\n"); + set_key = 1; + continue; + } + if (strlen(value) < MLAN_MIN_PASSPHRASE_LENGTH || + strlen(value) > MLAN_PMK_HEXSTR_LENGTH) { + PRINTM(MERROR, "Invalid PSK/PMK length\n"); + ret = -EINVAL; + goto done; + } + ap_cfg->wpa_cfg.length = strlen(value); + memcpy(ap_cfg->wpa_cfg.passphrase, value, strlen(value)); + set_key = 1; + } else if (!strncmp(opt, "CHANNEL", strlen("CHANNEL"))) { + if (set_chan) { + PRINTM(MWARN, "Skipping CHANNEL, found again!\n"); + continue; + } + if (woal_atoi(&atoi_ret, value)) { + ret = -EINVAL; + goto done; + } + if (atoi_ret < 1 || atoi_ret > MLAN_MAX_CHANNEL) { + PRINTM(MERROR, "AP_CFG: Channel must be between 1 and %d" + "(both included)\n", MLAN_MAX_CHANNEL); + ret = -EINVAL; + goto done; + } + ap_cfg->channel = atoi_ret; + set_chan = 1; + } else if (!strncmp(opt, "PREAMBLE", strlen("PREAMBLE"))) { + if (set_preamble) { + PRINTM(MWARN, "Skipping PREAMBLE, found again!\n"); + continue; + } + if (woal_atoi(&atoi_ret, value)) { + ret = -EINVAL; + goto done; + } + /* This is a READ only value from FW, so we can not set this and + pass it successfully */ + set_preamble = 1; + } else if (!strncmp(opt, "MAX_SCB", strlen("MAX_SCB"))) { + if (set_scb) { + PRINTM(MWARN, "Skipping MAX_SCB, found again!\n"); + continue; + } + if (woal_atoi(&atoi_ret, value)) { + ret = -EINVAL; + goto done; + } + if (atoi_ret < 1 || atoi_ret > MAX_STA_COUNT) { + PRINTM(MERROR, "AP_CFG: MAX_SCB must be between 1 to %d " + "(both included)\n", MAX_STA_COUNT); + ret = -EINVAL; + goto done; + } + ap_cfg->max_sta_count = (t_u16) atoi_ret; + set_scb = 1; + } else { + PRINTM(MERROR, "Invalid option %s\n", opt); + ret = -EINVAL; + goto done; + } + } + + done: + LEAVE(); + return ret; +} + +/** + * @brief Set AP configuration + * + * @param priv A pointer to moal_private structure + * @param data A pointer to user data + * @param len Length of buf + * + * @return 0 --success, otherwise fail + */ +int +woal_uap_set_ap_cfg(moal_private * priv, t_u8 * data, int len) +{ + int ret = 0; + static t_s8 buf[MAX_BUF_LEN]; + mlan_uap_bss_param sys_config; + int restart = 0; + + ENTER(); + +#define MIN_AP_CFG_CMD_LEN 16 /* strlen("ASCII_CMD=AP_CFG") */ + if ((len - 1) <= MIN_AP_CFG_CMD_LEN) { + PRINTM(MERROR, "Invalid length of command\n"); + ret = -EINVAL; + goto done; + } + + memset(buf, 0, MAX_BUF_LEN); + memcpy(buf, data, len); + + /* Initialize the uap bss values which are uploaded from firmware */ + woal_uap_get_bss_param(priv, &sys_config, MOAL_IOCTL_WAIT); + + /* Setting the default values */ + sys_config.channel = 6; + sys_config.preamble_type = 0; + + if ((ret = woal_uap_ap_cfg_parse_data(&sys_config, buf))) + goto done; + + /* If BSS already started stop it first and restart after changing the + setting */ + if (priv->bss_started == MTRUE) { + if ((ret = woal_uap_bss_ctrl(priv, MOAL_IOCTL_WAIT, UAP_BSS_STOP))) + goto done; + restart = 1; + } + + /* If the security mode is configured as WEP or WPA-PSK, it will disable + 11n automatically, and if configured as open(off) or wpa2-psk, it will + automatically enable 11n */ + if ((sys_config.protocol == PROTOCOL_STATIC_WEP) || + (sys_config.protocol == PROTOCOL_WPA)) { + if (MLAN_STATUS_SUCCESS != + woal_uap_set_11n_status(&sys_config, MLAN_ACT_DISABLE)) { + ret = -EFAULT; + goto done; + } + } else { + if (MLAN_STATUS_SUCCESS != + woal_uap_set_11n_status(&sys_config, MLAN_ACT_ENABLE)) { + ret = -EFAULT; + goto done; + } + } + + if (MLAN_STATUS_SUCCESS != + woal_set_get_sys_config(priv, MLAN_ACT_SET, MOAL_IOCTL_WAIT, + &sys_config)) { + ret = -EFAULT; + goto done; + } + + /* Start the BSS after successful configuration */ + if (restart) + ret = woal_uap_bss_ctrl(priv, MOAL_IOCTL_WAIT, UAP_BSS_START); + + done: + LEAVE(); + return ret; +} + +/** + * @brief uap BSS control ioctl handler + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option + * @param data BSS control type + * @return 0 --success, otherwise fail + */ +int +woal_uap_bss_ctrl(moal_private * priv, t_u8 wait_option, int data) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_bss *bss = NULL; + int ret = 0; + + ENTER(); + + PRINTM(MIOCTL, "ioctl bss ctrl=%d\n", data); + if ((data != UAP_BSS_START) && (data != UAP_BSS_STOP) && + (data != UAP_BSS_RESET)) { + PRINTM(MERROR, "Invalid parameter: %d\n", data); + ret = -EINVAL; + goto done; + } + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + bss = (mlan_ds_bss *) req->pbuf; + switch (data) { + case UAP_BSS_START: + if (priv->bss_started == MTRUE) { + PRINTM(MWARN, "Warning: BSS already started!\n"); + // goto done; + } else { + /* about to start bss: issue channel check */ + woal_11h_channel_check_ioctl(priv); + } + bss->sub_command = MLAN_OID_BSS_START; + break; + case UAP_BSS_STOP: + if (priv->bss_started == MFALSE) { + PRINTM(MWARN, "Warning: BSS already stopped!\n"); + // goto done; + } + bss->sub_command = MLAN_OID_BSS_STOP; + break; + case UAP_BSS_RESET: + bss->sub_command = MLAN_OID_UAP_BSS_RESET; + woal_cancel_cac_block(priv); + break; + } + 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 (data == UAP_BSS_STOP || data == UAP_BSS_RESET) + priv->bss_started = MFALSE; + + done: + if (req) + kfree(req); + + LEAVE(); + return ret; +} + +/** + * @brief This function sets multicast addresses to firmware + * + * @param dev A pointer to net_device structure + * @return N/A + */ +void +woal_uap_set_multicast_list(struct net_device *dev) +{ + ENTER(); + + LEAVE(); +} + +/** + * @brief ioctl function - entry point + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @param cmd Command + * + * @return 0 --success, otherwise fail + */ +int +woal_uap_do_ioctl(struct net_device *dev, struct ifreq *req, int cmd) +{ + int ret = 0; + ENTER(); + PRINTM(MIOCTL, "uap_do_ioctl: ioctl cmd = 0x%x\n", cmd); + switch (cmd) { + case UAP_HOSTCMD: + ret = woal_hostcmd_ioctl(dev, req); + break; + case UAP_IOCTL_CMD: + ret = woal_uap_ioctl(dev, req); + break; + case UAP_POWER_MODE: + ret = woal_uap_power_mode_ioctl(dev, req); + break; + case UAP_BSS_CTRL: + ret = woal_uap_bss_ctrl_ioctl(dev, req); + break; + case UAP_WAPI_MSG: + ret = woal_uap_set_wapi(dev, req); + break; + case UAP_BSS_CONFIG: + ret = woal_uap_bss_cfg_ioctl(dev, req); + break; + case UAP_STA_DEAUTH: + ret = woal_uap_sta_deauth_ioctl(dev, req); + break; + case UAP_RADIO_CTL: + ret = woal_uap_radio_ctl(dev, req); + break; + case UAP_GET_STA_LIST: + ret = woal_uap_get_sta_list_ioctl(dev, req); + break; + case UAP_CUSTOM_IE: + ret = woal_custom_ie_ioctl(dev, req); + break; + case UAP_GET_BSS_TYPE: + ret = woal_get_bss_type(dev, req); + break; + case WOAL_ANDROID_PRIV_CMD: + ret = woal_android_priv_cmd(dev, req); + break; + default: +#ifdef UAP_WEXT + ret = woal_uap_do_priv_ioctl(dev, req, cmd); +#else + ret = -EOPNOTSUPP; +#endif + break; + } + + LEAVE(); + return ret; +} + +/** + * @brief Get version + * + * @param priv A pointer to moal_private structure + * @param version A pointer to version buffer + * @param max_len max length of version buffer + * + * @return N/A + */ +void +woal_uap_get_version(moal_private * priv, char *version, int max_len) +{ + mlan_ds_get_info *info = NULL; + mlan_ioctl_req *req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_get_info)); + if (req == NULL) { + LEAVE(); + return; + } + + info = (mlan_ds_get_info *) req->pbuf; + info->sub_command = MLAN_OID_GET_VER_EXT; + req->req_id = MLAN_IOCTL_GET_INFO; + req->action = MLAN_ACT_GET; + + status = woal_request_ioctl(priv, req, MOAL_PROC_WAIT); + if (status == MLAN_STATUS_SUCCESS) { + PRINTM(MINFO, "MOAL UAP VERSION: %s\n", + info->param.ver_ext.version_str); + snprintf(version, max_len, driver_version, + info->param.ver_ext.version_str); + } + + if (req && (status != MLAN_STATUS_PENDING)) + kfree(req); + + LEAVE(); + return; +} + +/** + * @brief Get uap statistics + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option + * @param ustats A pointer to mlan_ds_uap_stats structure + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail + */ +mlan_status +woal_uap_get_stats(moal_private * priv, t_u8 wait_option, + mlan_ds_uap_stats * ustats) +{ + mlan_ds_get_info *info = NULL; + mlan_ioctl_req *req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_get_info)); + if (req == NULL) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + info = (mlan_ds_get_info *) req->pbuf; + info->sub_command = MLAN_OID_GET_STATS; + req->req_id = MLAN_IOCTL_GET_INFO; + req->action = MLAN_ACT_GET; + + status = woal_request_ioctl(priv, req, wait_option); + if (status == MLAN_STATUS_SUCCESS) { + if (ustats) + memcpy(ustats, &info->param.ustats, sizeof(mlan_ds_uap_stats)); +#ifdef UAP_WEXT + priv->w_stats.discard.fragment = info->param.ustats.fcs_error_count; + priv->w_stats.discard.retries = info->param.ustats.retry_count; + priv->w_stats.discard.misc = info->param.ustats.ack_failure_count; +#endif + } + + if (req && (status != MLAN_STATUS_PENDING)) + kfree(req); + + LEAVE(); + return status; +} + +/** + * @brief Set/Get system configuration parameters + * + * @param priv A pointer to moal_private structure + * @param action MLAN_ACT_SET or MLAN_ACT_GET + * @param wait_option Wait option + * @param sys_cfg A pointer to mlan_uap_bss_param structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +woal_set_get_sys_config(moal_private * priv, t_u16 action, t_u8 wait_option, + mlan_uap_bss_param * sys_cfg) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + 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 = MLAN_STATUS_FAILURE; + goto done; + } + + bss = (mlan_ds_bss *) req->pbuf; + bss->sub_command = MLAN_OID_UAP_BSS_CONFIG; + req->req_id = MLAN_IOCTL_BSS; + req->action = action; + + if (action == MLAN_ACT_SET) { + memcpy(&bss->param.bss_config, sys_cfg, sizeof(mlan_uap_bss_param)); + } + + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, wait_option)) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + if (action == MLAN_ACT_GET) { + memcpy(sys_cfg, &bss->param.bss_config, sizeof(mlan_uap_bss_param)); + } + + done: + if (req) + kfree(req); + + LEAVE(); + return ret; +} + +/** + * @brief Set invalid data for each member of mlan_uap_bss_param + * structure + * + * @param config A pointer to mlan_uap_bss_param structure + * + * @return N/A + */ +void +woal_set_sys_config_invalid_data(mlan_uap_bss_param * config) +{ + ENTER(); + + memset(config, 0, sizeof(mlan_uap_bss_param)); + config->bcast_ssid_ctl = 0x7F; + config->radio_ctl = 0x7F; + config->dtim_period = 0x7F; + config->beacon_period = 0x7FFF; + config->tx_data_rate = 0x7FFF; + config->mcbc_data_rate = 0x7FFF; + config->tx_power_level = 0x7F; + config->tx_antenna = 0x7F; + config->rx_antenna = 0x7F; + config->pkt_forward_ctl = 0x7F; + config->max_sta_count = 0x7FFF; + config->auth_mode = 0x7F; + config->sta_ageout_timer = 0x7FFFFFFF; + config->pairwise_update_timeout = 0x7FFFFFFF; + config->pwk_retries = 0x7FFFFFFF; + config->groupwise_update_timeout = 0x7FFFFFFF; + config->gwk_retries = 0x7FFFFFFF; + config->mgmt_ie_passthru_mask = 0x7FFFFFFF; + config->ps_sta_ageout_timer = 0x7FFFFFFF; + config->rts_threshold = 0x7FFF; + config->frag_threshold = 0x7FFF; + config->retry_limit = 0x7FFF; + config->filter.filter_mode = 0x7FFF; + config->filter.mac_count = 0x7FFF; + config->wpa_cfg.rsn_protection = 0x7F; + config->wpa_cfg.gk_rekey_time = 0x7FFFFFFF; + config->enable_2040coex = 0x7F; + config->wmm_para.qos_info = 0x7F; + + LEAVE(); +} |