/** @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(); }