/** @file moal_uap.c * * @brief This file contains the major functions in UAP * driver. * * * Copyright 2008-2022 NXP * * NXP CONFIDENTIAL * The source code contained or described herein and all documents related to * the source code (Materials) are owned by NXP, its * suppliers and/or its licensors. Title to the Materials remains with NXP, * its suppliers and/or its licensors. The Materials contain * trade secrets and proprietary and confidential information of NXP, its * suppliers and/or its licensors. The Materials are protected by worldwide * copyright and trade secret laws and treaty provisions. No part of the * Materials may be used, copied, reproduced, modified, published, uploaded, * posted, transmitted, distributed, or disclosed in any way without NXP's prior * express written permission. * * No license under any patent, copyright, trade secret or other intellectual * property right is granted to or conferred upon you by disclosure or delivery * of the Materials, either expressly, by implication, inducement, estoppel or * otherwise. Any license under such intellectual property rights must be * express and approved by NXP in writing. * * Alternatively, this software may be distributed under the terms of GPL v2. * SPDX-License-Identifier: GPL-2.0 * */ /******************************************************** Change log: 10/21/2008: initial version ********************************************************/ #include "moal_main.h" #include "moal_uap.h" #ifdef SDIO #include "moal_sdio.h" #endif /* SDIO */ #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; uap_addba_param param; int ret = 0; mlan_status status = MLAN_STATUS_SUCCESS; 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; } status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { 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 (status != MLAN_STATUS_PENDING) 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; uap_aggr_prio_tbl param; int ret = 0; int i = 0; mlan_status status = MLAN_STATUS_SUCCESS; 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 *)¶m, 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]; } } status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { 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 (status != MLAN_STATUS_PENDING) 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; mlan_status status = MLAN_STATUS_SUCCESS; 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 *)¶m, 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]; } status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { 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 (status != MLAN_STATUS_PENDING) 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); uap_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; fw.fw_bands = fw_info.fw_bands; fw.region_code = fw_info.region_code; fw.hw_dot_11n_dev_cap = fw_info.hw_dot_11n_dev_cap; /* 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; mlan_status status = MLAN_STATUS_SUCCESS; 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 *)¶m, 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; } } status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { 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 (status != MLAN_STATUS_PENDING) kfree(ioctl_req); LEAVE(); return ret; } /** * @brief configure band steering * * @param dev A pointer to net_device structure * @param req A pointer to ifreq structure * @return 0 --success, otherwise fail */ static int woal_uap_band_steer(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 *pm = NULL; band_steer_para param; int ret = 0; mlan_status status = MLAN_STATUS_SUCCESS; ENTER(); memset(¶m, 0, sizeof(param)); /* Sanity check */ if (req->ifr_data == NULL) { PRINTM(MERROR, "woal_uap_band_steer() 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, "band_steer_para", (t_u8 *)¶m, sizeof(param)); ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_band_steer_cfg)); if (ioctl_req == NULL) { LEAVE(); return -ENOMEM; } pm = (mlan_ds_misc_cfg *)ioctl_req->pbuf; pm->sub_command = MLAN_OID_MISC_BAND_STEERING; ioctl_req->req_id = MLAN_IOCTL_MISC_CFG; pm->param.band_steer_cfg.action = param.action; pm->param.band_steer_cfg.block_2g_prb_req = param.block_2g_prb_req; pm->param.band_steer_cfg.state = param.state; pm->param.band_steer_cfg.max_btm_req_allowed = param.max_btm_req_allowed; status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto done; } param.action = pm->param.band_steer_cfg.action; param.block_2g_prb_req = pm->param.band_steer_cfg.block_2g_prb_req; param.state = pm->param.band_steer_cfg.state; param.max_btm_req_allowed = pm->param.band_steer_cfg.max_btm_req_allowed; /* 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 (status != MLAN_STATUS_PENDING) kfree(ioctl_req); LEAVE(); return ret; } /** * @brief configure beacon stuck detect mechanism * * @param dev A pointer to net_device structure * @param req A pointer to ifreq structure * @return 0 --success, otherwise fail */ static int woal_uap_beacon_stuck(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 *pm = NULL; beacon_stuck_detect_para param; int ret = 0; mlan_status status = MLAN_STATUS_SUCCESS; ENTER(); memset(¶m, 0, sizeof(param)); /* Sanity check */ if (req->ifr_data == NULL) { PRINTM(MERROR, "woal_uap_beacon_stuck() 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, "beacon_stuck_detect_para", (t_u8 *)¶m, sizeof(param)); ioctl_req = woal_alloc_mlan_ioctl_req( sizeof(mlan_ds_beacon_stuck_param_cfg)); if (ioctl_req == NULL) { LEAVE(); return -ENOMEM; } pm = (mlan_ds_misc_cfg *)ioctl_req->pbuf; pm->sub_command = MLAN_OID_MISC_BEACON_STUCK; ioctl_req->req_id = MLAN_IOCTL_MISC_CFG; pm->param.beacon_stuck_cfg.action = param.action; pm->param.beacon_stuck_cfg.beacon_stuck_detect_count = param.beacon_stuck_detect_count; pm->param.beacon_stuck_cfg.recovery_confirm_count = param.recovery_confirm_count; status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto done; } param.action = pm->param.beacon_stuck_cfg.action; param.beacon_stuck_detect_count = pm->param.beacon_stuck_cfg.beacon_stuck_detect_count; param.recovery_confirm_count = pm->param.beacon_stuck_cfg.recovery_confirm_count; /* 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 (status != MLAN_STATUS_PENDING) 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; mlan_status status = MLAN_STATUS_SUCCESS; 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 *)¶m, 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; } status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { 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 (status != MLAN_STATUS_PENDING) kfree(ioctl_req); LEAVE(); return ret; } #ifdef SDIO /** * @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); #ifdef SDIO_MMC 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; } #else if (sdio_read_ioreg(priv->phandle->card, func, reg, &data) < 0) { PRINTM(MERROR, "sdio_read_ioreg: reading register 0x%X failed\n", reg); ret = MLAN_STATUS_FAILURE; goto done; } #endif 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); #ifdef SDIO_MMC 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; } #else if (sdio_write_ioreg(priv->phandle->card, func, reg, data) < 0) { PRINTM(MERROR, "sdio_write_ioreg: writing register 0x%X failed\n", reg); ret = MLAN_STATUS_FAILURE; goto done; } #endif } /* 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; } #endif /** * @brief enable/disable 11h * * @param enable MTRUE/MFALSE * @return 0 --success, otherwise fail */ int woal_uap_11h_ctrl(moal_private *priv, t_u32 enable) { mlan_ioctl_req *ioctl_req = NULL; mlan_ds_snmp_mib *snmp = NULL; int ret = 0; mlan_status status = MLAN_STATUS_SUCCESS; ENTER(); 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; snmp->sub_command = MLAN_OID_SNMP_MIB_DOT11H; snmp->param.oid_value = enable; status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto done; } done: if (status != MLAN_STATUS_PENDING) kfree(ioctl_req); 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; mlan_status status = MLAN_STATUS_SUCCESS; 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 *)¶m, 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", __func__, 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; } status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { 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 (status != MLAN_STATUS_PENDING) 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; mlan_status status = MLAN_STATUS_SUCCESS; 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 *)¶m, 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) > (int)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; moal_memcpy_ext(priv->phandle, cfg11d->param.domain_tlv, tlv, TLV_HEADER_LEN + tlv_data_len, sizeof(cfg11d->param.domain_tlv)); } status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { 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 (status != MLAN_STATUS_PENDING) kfree(ioctl_req); LEAVE(); return ret; } /** * @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; mlan_status status = MLAN_STATUS_SUCCESS; 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 *)¶m, 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 * 1000; 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; cfg11h->param.dfs_testing.usr_cac_restart = param.cac_restart; priv->phandle->cac_restart = param.cac_restart; priv->phandle->cac_period_jiffies = param.usr_cac_period * HZ; priv->user_cac_period_msec = cfg11h->param.dfs_testing.usr_cac_period_msec; } status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto done; } if (!param.action) { /* GET */ param.usr_cac_period = cfg11h->param.dfs_testing.usr_cac_period_msec / 1000; 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; param.cac_restart = cfg11h->param.dfs_testing.usr_cac_restart; } /* 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 (status != MLAN_STATUS_PENDING) kfree(ioctl_req); LEAVE(); return ret; } #ifdef UAP_CFG80211 #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) /** * @brief uap channel NOP status check 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_get_channel_nop_info(moal_private *priv, t_u8 wait_option, mlan_ds_11h_chan_nop_info *ch_info) { mlan_ioctl_req *req = NULL; mlan_ds_11h_cfg *ds_11hcfg = NULL; int ret = 0; mlan_status status = MLAN_STATUS_SUCCESS; ENTER(); if (!ch_info) { PRINTM(MERROR, "Invalid chan_info\n"); LEAVE(); return -EFAULT; } req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11h_cfg)); if (req == NULL) { ret = -ENOMEM; goto done; } req->req_id = MLAN_IOCTL_11H_CFG; req->action = MLAN_ACT_GET; ds_11hcfg = (mlan_ds_11h_cfg *)req->pbuf; ds_11hcfg->sub_command = MLAN_OID_11H_CHAN_NOP_INFO; moal_memcpy_ext(priv->phandle, &ds_11hcfg->param.ch_nop_info, ch_info, sizeof(mlan_ds_11h_chan_nop_info), sizeof(ds_11hcfg->param.ch_nop_info)); status = woal_request_ioctl(priv, req, wait_option); if (status == MLAN_STATUS_FAILURE) { ret = -EFAULT; goto done; } moal_memcpy_ext(priv->phandle, ch_info, &ds_11hcfg->param.ch_nop_info, sizeof(mlan_ds_11h_chan_nop_info), sizeof(mlan_ds_11h_chan_nop_info)); done: if (status != MLAN_STATUS_PENDING) kfree(req); LEAVE(); return ret; } #endif #endif /** * @brief configure channel switch count * * @param dev A pointer to net_device structure * @param req A pointer to ifreq structure * @return 0 --success, otherwise fail */ static int woal_uap_chan_switch_count_cfg(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; cscount_cfg_t param; int ret = 0; mlan_status status = MLAN_STATUS_SUCCESS; ENTER(); memset(¶m, 0, sizeof(param)); /* Sanity check */ if (req->ifr_data == NULL) { PRINTM(MERROR, "%s corrupt data\n", __func__); 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, "cscount_cfg_t", (t_u8 *)¶m, 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_CHAN_SWITCH_COUNT; 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.cs_count = param.cs_count; } status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto done; } if (!param.action) { /* GET */ param.cs_count = cfg11h->param.cs_count; } /* 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 (status != MLAN_STATUS_PENDING) kfree(ioctl_req); LEAVE(); return ret; } /** * @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 Set/Get 11n configurations * * @param dev A pointer to net_device structure * @param req A pointer to ifreq structure * @return 0 --success, otherwise fail */ static int woal_uap_ht_tx_cfg(struct net_device *dev, struct ifreq *req) { int ret = 0; moal_private *priv = (moal_private *)netdev_priv(dev); mlan_ds_11n_cfg *cfg_11n = NULL; mlan_ds_11n_tx_cfg httx_cfg; mlan_ioctl_req *ioctl_req = NULL; ht_tx_cfg_para_hdr param; mlan_status status = MLAN_STATUS_SUCCESS; ENTER(); memset(¶m, 0, sizeof(ht_tx_cfg_para_hdr)); memset(&httx_cfg, 0, sizeof(mlan_ds_11n_tx_cfg)); /* Sanity check */ if (req->ifr_data == NULL) { PRINTM(MERROR, "woal_uap_ht_tx_cfg corrupt data\n"); ret = -EFAULT; goto done; } if (copy_from_user(¶m, req->ifr_data, sizeof(ht_tx_cfg_para_hdr))) { PRINTM(MERROR, "Copy from user failed\n"); ret = -EFAULT; goto done; } ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); if (ioctl_req == NULL) { ret = -ENOMEM; goto done; } cfg_11n = (mlan_ds_11n_cfg *)ioctl_req->pbuf; cfg_11n->sub_command = MLAN_OID_11N_CFG_TX; ioctl_req->req_id = MLAN_IOCTL_11N_CFG; if (copy_from_user(&httx_cfg, req->ifr_data + sizeof(ht_tx_cfg_para_hdr), sizeof(mlan_ds_11n_tx_cfg))) { PRINTM(MERROR, "Copy from user failed\n"); ret = -EFAULT; goto done; } if (!param.action) { /* Get 11n tx parameters from MLAN */ ioctl_req->action = MLAN_ACT_GET; } else { /* Set HT Tx configurations */ cfg_11n->param.tx_cfg.httxcap = httx_cfg.httxcap; PRINTM(MINFO, "SET: httxcap:0x%x\n", httx_cfg.httxcap); /* Update 11n tx parameters in MLAN */ ioctl_req->action = MLAN_ACT_SET; } status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto done; } if (ioctl_req->action == MLAN_ACT_GET) { httx_cfg.httxcap = cfg_11n->param.tx_cfg.httxcap; PRINTM(MINFO, "GET: httxcap:0x%x\n", httx_cfg.httxcap); } /* Copy to user */ if (copy_to_user(req->ifr_data + sizeof(ht_tx_cfg_para_hdr), &httx_cfg, sizeof(mlan_ds_11n_tx_cfg))) { PRINTM(MERROR, "Copy to user failed!\n"); ret = -EFAULT; goto done; } done: if (status != MLAN_STATUS_PENDING) kfree(ioctl_req); LEAVE(); return ret; } /** * @brief Set/Get Set/Get 11AC configurations * * @param dev A pointer to net_device structure * @param req A pointer to ifreq structure * @return 0 --success, otherwise fail */ static int woal_uap_vht_cfg(struct net_device *dev, struct ifreq *req) { moal_private *priv = (moal_private *)netdev_priv(dev); int ret = 0, resbuf_len = 0; mlan_ds_11ac_cfg *cfg_11ac = NULL; mlan_ioctl_req *ioctl_req = NULL; mlan_ds_11ac_vht_cfg *vhtcfg = NULL, vht_cfg; t_u8 *respbuf = NULL; vht_cfg_para_hdr param; mlan_status status = MLAN_STATUS_SUCCESS; #define CMD_RESPBUF_LEN 2048 gfp_t flag; ENTER(); memset(¶m, 0, sizeof(vht_cfg_para_hdr)); flag = (in_atomic() || irqs_disabled()) ? GFP_ATOMIC : GFP_KERNEL; respbuf = kzalloc(CMD_RESPBUF_LEN, flag); if (!respbuf) { ret = -ENOMEM; goto done; } /* Sanity check */ if (req->ifr_data == NULL) { PRINTM(MERROR, "woal_uap_ht_tx_cfg corrupt data\n"); ret = -EFAULT; goto done; } if (copy_from_user(¶m, req->ifr_data, sizeof(vht_cfg_para_hdr))) { PRINTM(MERROR, "Copy from user failed\n"); ret = -EFAULT; goto done; } ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11ac_cfg)); if (ioctl_req == NULL) { ret = -ENOMEM; goto done; } cfg_11ac = (mlan_ds_11ac_cfg *)ioctl_req->pbuf; cfg_11ac->sub_command = MLAN_OID_11AC_VHT_CFG; ioctl_req->req_id = MLAN_IOCTL_11AC_CFG; if (copy_from_user(&vht_cfg, req->ifr_data + sizeof(vht_cfg_para_hdr), sizeof(mlan_ds_11ac_vht_cfg))) { PRINTM(MERROR, "Copy from user failed\n"); ret = -EFAULT; goto done; } if (vht_cfg.band == BAND_SELECT_BOTH) { cfg_11ac->param.vht_cfg.band = (BAND_SELECT_BG | BAND_SELECT_A); } else { cfg_11ac->param.vht_cfg.band = vht_cfg.band; } if (!param.action) { /* GET operation */ if (vht_cfg.band == BAND_SELECT_BOTH) { /* if get both bands, get BG first */ cfg_11ac->param.vht_cfg.band = BAND_SELECT_BG; } PRINTM(MINFO, "GET: vhtcfg band: 0x%x\n", cfg_11ac->param.vht_cfg.band); if (priv->bss_role == MLAN_BSS_ROLE_UAP) cfg_11ac->param.vht_cfg.txrx = MLAN_RADIO_RX; else cfg_11ac->param.vht_cfg.txrx = vht_cfg.txrx; PRINTM(MINFO, "GET: vhtcfg txrx: 0x%x\n", cfg_11ac->param.vht_cfg.txrx); ioctl_req->action = MLAN_ACT_GET; } else { /* Band */ if (vht_cfg.band == BAND_SELECT_BOTH) cfg_11ac->param.vht_cfg.band = (BAND_SELECT_BG | BAND_SELECT_A); else cfg_11ac->param.vht_cfg.band = vht_cfg.band; PRINTM(MINFO, "SET: vhtcfg band: 0x%x\n", cfg_11ac->param.vht_cfg.band); /* Tx/Rx */ cfg_11ac->param.vht_cfg.txrx = vht_cfg.txrx; PRINTM(MINFO, "SET: vhtcfg txrx: 0x%x\n", cfg_11ac->param.vht_cfg.txrx); /* BW cfg */ cfg_11ac->param.vht_cfg.bwcfg = vht_cfg.bwcfg; PRINTM(MINFO, "SET: vhtcfg bw cfg:0x%x\n", cfg_11ac->param.vht_cfg.bwcfg); cfg_11ac->param.vht_cfg.vht_cap_info = vht_cfg.vht_cap_info; PRINTM(MINFO, "SET: vhtcfg vht_cap_info:0x%x\n", cfg_11ac->param.vht_cfg.vht_cap_info); cfg_11ac->param.vht_cfg.vht_tx_mcs = vht_cfg.vht_tx_mcs; cfg_11ac->param.vht_cfg.vht_rx_mcs = vht_cfg.vht_rx_mcs; /* Update 11AC parameters in MLAN */ ioctl_req->action = MLAN_ACT_SET; } status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto done; } /* number of vhtcfg entries */ *respbuf = 1; vhtcfg = (mlan_ds_11ac_vht_cfg *)(respbuf + 1); moal_memcpy_ext(priv->phandle, vhtcfg, &cfg_11ac->param.vht_cfg, sizeof(mlan_ds_11ac_vht_cfg), sizeof(mlan_ds_11ac_vht_cfg)); resbuf_len = 1 + sizeof(mlan_ds_11ac_vht_cfg); if ((ioctl_req->action == MLAN_ACT_GET) && (vht_cfg.band == BAND_SELECT_BOTH)) { cfg_11ac->param.vht_cfg.band = BAND_SELECT_A; status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto done; } /* number of vhtcfg entries */ *respbuf = 2; vhtcfg++; moal_memcpy_ext(priv->phandle, vhtcfg, &cfg_11ac->param.vht_cfg, sizeof(mlan_ds_11ac_vht_cfg), sizeof(mlan_ds_11ac_vht_cfg)); resbuf_len += sizeof(mlan_ds_11ac_vht_cfg); } if (ioctl_req->action == MLAN_ACT_GET) { if (copy_to_user(req->ifr_data, respbuf, resbuf_len)) { PRINTM(MERROR, "Copy to user failed\n"); ret = -EFAULT; } } done: if (status != MLAN_STATUS_PENDING) kfree(ioctl_req); if (respbuf) kfree(respbuf); 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 = NULL; 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; } sys_config = kzalloc(sizeof(mlan_uap_bss_param), GFP_ATOMIC); if (!sys_config) { PRINTM(MERROR, "Fail to alloc memory for mlan_uap_bss_param\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: kfree(sys_config); 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; mlan_status status = MLAN_STATUS_SUCCESS; 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 { if ((tx_rate_config.user_data_cnt <= 0) || (tx_rate_config.user_data_cnt > 4)) { PRINTM(MERROR, "Invalid user_data_cnt\n"); ret = -EINVAL; goto done; } mreq->action = MLAN_ACT_SET; if (tx_rate_config.rate_format == AUTO_RATE) rate->param.rate_cfg.is_rate_auto = 1; else { if ((tx_rate_config.rate_format < 0) || (tx_rate_config.rate < 0)) { PRINTM(MERROR, "Invalid format or rate selection\n"); ret = -EINVAL; goto done; } /* rate_format sanity check */ if ((tx_rate_config.rate_format > MLAN_RATE_FORMAT_HE)) { PRINTM(MERROR, "Invalid format selection\n"); ret = -EINVAL; goto done; } rate->param.rate_cfg.rate_format = tx_rate_config.rate_format; /* rate sanity check */ if (tx_rate_config.user_data_cnt >= 2) { if (((tx_rate_config.rate_format == MLAN_RATE_FORMAT_LG) && (tx_rate_config.rate > MLAN_RATE_INDEX_OFDM7)) || ((tx_rate_config.rate_format == MLAN_RATE_FORMAT_HT) && (tx_rate_config.rate != 32) && (tx_rate_config.rate > 15)) || ((tx_rate_config.rate_format == MLAN_RATE_FORMAT_VHT) && (tx_rate_config.rate > MLAN_RATE_INDEX_MCS9)) || ((tx_rate_config.rate_format == MLAN_RATE_FORMAT_HE) && (tx_rate_config.rate > MLAN_RATE_INDEX_MCS11))) { PRINTM(MERROR, "Invalid rate selection\n"); ret = -EINVAL; goto done; } rate->param.rate_cfg.rate = tx_rate_config.rate; } /* nss sanity check */ if ((tx_rate_config.rate_format == 2) || (tx_rate_config.rate_format == 3)) { if ((tx_rate_config.nss < 1) || (tx_rate_config.nss > 2)) { PRINTM(MERROR, "Invalid nss selection %d\n", tx_rate_config.nss); ret = -EINVAL; goto done; } rate->param.rate_cfg.nss = tx_rate_config.nss; } if (tx_rate_config.user_data_cnt <= 3) rate->param.rate_cfg.rate_setting = 0xffff; else rate->param.rate_cfg.rate_setting = tx_rate_config.rate_setting; } } status = woal_request_ioctl(priv, mreq, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { 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_format = AUTO_RATE; else { /* fixed rate */ tx_rate_config.rate_format = rate->param.rate_cfg.rate_format; tx_rate_config.rate = rate->param.rate_cfg.rate; if (rate->param.rate_cfg.rate_format == MLAN_RATE_FORMAT_VHT || rate->param.rate_cfg.rate_format == MLAN_RATE_FORMAT_HE) tx_rate_config.nss = rate->param.rate_cfg.nss; tx_rate_config.rate_setting = rate->param.rate_cfg.rate_setting; } 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 (status != MLAN_STATUS_PENDING) 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; mlan_status status = MLAN_STATUS_SUCCESS; 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 defined(STA_CFG80211) || defined(UAP_CFG80211) if (IS_CARD9098(priv->phandle->card_type) || IS_CARD9097(priv->phandle->card_type)) { if (IS_STA_CFG80211( priv->phandle->params.cfg80211_wext)) woal_cfg80211_notify_antcfg( priv, priv->phandle->wiphy, radio); } #endif } status = woal_request_ioctl(priv, mreq, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { 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 (status != MLAN_STATUS_PENDING) kfree(mreq); LEAVE(); return ret; } /** * @brief Set/Get HT stream configurations * * @param dev A pointer to net_device structure * @param req A pointer to ifreq structure * * @return 0 --success, otherwise fail */ static int woal_uap_htstream_cfg(struct net_device *dev, struct ifreq *req) { moal_private *priv = (moal_private *)netdev_priv(dev); int ret = 0; mlan_ds_11n_cfg *cfg = NULL; mlan_ioctl_req *ioctl_req = NULL; htstream_cfg_t htstream_cfg; mlan_status status = MLAN_STATUS_SUCCESS; ENTER(); memset(&htstream_cfg, 0, sizeof(htstream_cfg_t)); /* Sanity check */ if (req->ifr_data == NULL) { PRINTM(MERROR, "woal_uap_htstream_cfg corrupt data\n"); ret = -EFAULT; goto done; } if (copy_from_user(&htstream_cfg, req->ifr_data, sizeof(htstream_cfg_t))) { PRINTM(MERROR, "Copy from user failed\n"); ret = -EFAULT; goto done; } ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); if (ioctl_req == NULL) { ret = -ENOMEM; goto done; } cfg = (mlan_ds_11n_cfg *)ioctl_req->pbuf; cfg->sub_command = MLAN_OID_11N_CFG_STREAM_CFG; ioctl_req->req_id = MLAN_IOCTL_11N_CFG; if (!htstream_cfg.action) { /* Get operation */ ioctl_req->action = MLAN_ACT_GET; } else { /* Update HT stream parameter in MLAN */ ioctl_req->action = MLAN_ACT_SET; /* Set HT Stream configuration */ cfg->param.stream_cfg = htstream_cfg.stream_cfg; PRINTM(MINFO, "SET: htstream_cfg:0x%x\n", cfg->param.stream_cfg); } status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto done; } /* Copy to user */ if (ioctl_req->action == MLAN_ACT_GET) { PRINTM(MINFO, "GET: htstream_cfg:0x%x\n", htstream_cfg.stream_cfg); htstream_cfg.stream_cfg = cfg->param.stream_cfg; if (copy_to_user(req->ifr_data, &htstream_cfg, sizeof(htstream_cfg_t))) { PRINTM(MERROR, "Copy to user failed!\n"); ret = -EFAULT; goto done; } } done: if (status != MLAN_STATUS_PENDING) kfree(ioctl_req); LEAVE(); return ret; } /** * @brief Get DFS_REPEATER 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_dfs_repeater(struct net_device *dev, struct ifreq *req) { moal_private *priv = (moal_private *)netdev_priv(dev); int ret = 0; dfs_repeater_mode param; mlan_ds_misc_cfg *misc = NULL; mlan_ioctl_req *mreq = NULL; mlan_status status = MLAN_STATUS_SUCCESS; ENTER(); /* Sanity check */ if (req->ifr_data == NULL) { PRINTM(MERROR, "uap_antenna_cfg() corrupt data\n"); ret = -EFAULT; goto done; } memset(¶m, 0, sizeof(dfs_repeater_mode)); /* Get user data */ if (copy_from_user(¶m, req->ifr_data, sizeof(dfs_repeater_mode))) { PRINTM(MERROR, "Copy from user failed\n"); ret = -EFAULT; goto done; } mreq = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); if (mreq == NULL) { ret = -ENOMEM; goto done; } misc = (mlan_ds_misc_cfg *)mreq->pbuf; misc->sub_command = MLAN_OID_MISC_DFS_REAPTER_MODE; mreq->req_id = MLAN_IOCTL_MISC_CFG; mreq->action = MLAN_ACT_GET; status = woal_request_ioctl(priv, mreq, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto done; } param.mode = misc->param.dfs_repeater.mode; if (copy_to_user(req->ifr_data, ¶m, sizeof(dfs_repeater_mode))) { PRINTM(MERROR, "Copy to user failed\n"); ret = -EFAULT; } done: if (status != MLAN_STATUS_PENDING) kfree(mreq); LEAVE(); return ret; } /** * @brief Issue MLAN_OID_11H_CHAN_REPORT_REQUEST ioctl to cancel dozer * * @param priv Pointer to the moal_private driver data struct * @param action MLAN_ACT_SET/MLAN_ACT_GET * @param * * @return 0 --success, otherwise fail */ int woal_11h_chan_dfs_state(moal_private *priv, t_u8 action, mlan_ds_11h_chan_dfs_state *ch_dfs_state) { int ret = 0; mlan_ioctl_req *req = NULL; mlan_ds_11h_cfg *ds_11hcfg = NULL; mlan_status status = MLAN_STATUS_SUCCESS; #ifdef UAP_CFG80211 #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) int cfg80211_wext = priv->phandle->params.cfg80211_wext; #endif #endif ENTER(); #ifdef UAP_CFG80211 #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) if (action == MLAN_ACT_GET) { if (IS_UAP_CFG80211(cfg80211_wext)) { ret = woal_get_wiphy_chan_dfs_state(priv->wdev->wiphy, ch_dfs_state); if (!ret) { LEAVE(); return ret; } } } #endif #endif req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11h_cfg)); if (req == NULL) { ret = -ENOMEM; goto done; } ds_11hcfg = (mlan_ds_11h_cfg *)req->pbuf; ds_11hcfg->sub_command = MLAN_OID_11H_CHAN_DFS_STATE; req->req_id = MLAN_IOCTL_11H_CFG; req->action = action; moal_memcpy_ext(priv->phandle, &ds_11hcfg->param.ch_dfs_state, ch_dfs_state, sizeof(mlan_ds_11h_chan_dfs_state), sizeof(ds_11hcfg->param.ch_dfs_state)); /* Send Channel Check command and wait until the report is ready */ status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto done; } moal_memcpy_ext(priv->phandle, ch_dfs_state, &ds_11hcfg->param.ch_dfs_state, sizeof(mlan_ds_11h_chan_dfs_state), sizeof(mlan_ds_11h_chan_dfs_state)); done: if (status != MLAN_STATUS_PENDING) kfree(req); LEAVE(); return ret; } /** * @brief skip cac on specific channel * @and Wext * * @param dev A pointer to net_device structure * @param req A pointer to ifreq structure * * @return 0 --success, otherwise fail */ static int woal_uap_skip_cac(struct net_device *dev, struct ifreq *req) { int ret = 0; skip_cac_para param; moal_private *priv = (moal_private *)netdev_priv(dev); #ifdef UAP_CFG80211 #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) int cfg80211_wext = priv->phandle->params.cfg80211_wext; #endif #endif dfs_state_t dfs_state; mlan_ds_11h_chan_dfs_state ch_dfs_state; ENTER(); /* Sanity check */ if (req->ifr_data == NULL) { PRINTM(MERROR, "skip_dfs_cac() corrupt data\n"); ret = -EFAULT; goto done; } memset(¶m, 0, sizeof(skip_cac_para)); /* Get user data */ if (copy_from_user(¶m, req->ifr_data, sizeof(skip_cac_para))) { PRINTM(MERROR, "Copy from user failed\n"); ret = -EFAULT; goto done; } if (param.skip_cac) dfs_state = DFS_AVAILABLE; else dfs_state = DFS_USABLE; memset(&ch_dfs_state, 0, sizeof(ch_dfs_state)); ch_dfs_state.channel = param.channel; woal_11h_chan_dfs_state(priv, MLAN_ACT_GET, &ch_dfs_state); if (ch_dfs_state.dfs_state == dfs_state) goto done; if (param.skip_cac && ch_dfs_state.dfs_state == DFS_USABLE) PRINTM(MMSG, "ZeroDFS: Requst skip cac on the channel %d which hasn't do CAC before!\n", param.channel); ch_dfs_state.dfs_state = dfs_state; woal_11h_chan_dfs_state(priv, MLAN_ACT_SET, &ch_dfs_state); PRINTM(MCMND, "ZeroDFS: Skip CAC on chan %d %d\n", param.channel, param.skip_cac); #ifdef UAP_CFG80211 #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) if (IS_UAP_CFG80211(cfg80211_wext)) { if (param.skip_cac) woal_update_channel_dfs_state(param.channel, DFS_AVAILABLE); else woal_update_channel_dfs_state(param.channel, DFS_USABLE); } #endif #endif done: LEAVE(); return ret; } /** * @brief Get DFS_REPEATER 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_cac_timer_status(struct net_device *dev, struct ifreq *req) { moal_private *priv = (moal_private *)netdev_priv(dev); int ret = 0; cac_timer_status param; mlan_status status = MLAN_STATUS_SUCCESS; ENTER(); /* Sanity check */ if (req->ifr_data == NULL) { PRINTM(MERROR, "uap_antenna_cfg() corrupt data\n"); ret = -EFAULT; goto done; } memset(¶m, 0, sizeof(cac_timer_status)); /* Get user data */ if (copy_from_user(¶m, req->ifr_data, sizeof(cac_timer_status))) { PRINTM(MERROR, "Copy from user failed\n"); ret = -EFAULT; goto done; } /* Currently default action is get */ param.mode = 0; if (priv->phandle->cac_period == MTRUE) { long cac_left_jiffies; cac_left_jiffies = MEAS_REPORT_TIME - (jiffies - priv->phandle->meas_start_jiffies); /* cac_left_jiffies would be negative if timer has already * elapsed. positive if timer is still yet to lapsed */ if (cac_left_jiffies > 0) param.mode = (t_u32)cac_left_jiffies / HZ; } if (copy_to_user(req->ifr_data, ¶m, sizeof(cac_timer_status))) { PRINTM(MERROR, "Copy to user failed\n"); ret = -EFAULT; } done: if (status != MLAN_STATUS_PENDING) LEAVE(); return ret; } /** * @brief set/get uap operation control * * @param dev A pointer to net_device structure * @param req A pointer to ifreq structure * * @return 0 --success, otherwise fail */ static int woal_uap_operation_ctrl(struct net_device *dev, struct ifreq *req) { int ret = 0; moal_private *priv = (moal_private *)netdev_priv(dev); mlan_uap_oper_ctrl uap_oper; uap_oper_para_hdr param; mlan_ds_bss *bss = NULL; mlan_ioctl_req *ioctl_req = NULL; mlan_status status = MLAN_STATUS_SUCCESS; ENTER(); memset(¶m, 0, sizeof(uap_oper_para_hdr)); memset(&uap_oper, 0, sizeof(mlan_uap_oper_ctrl)); /* Sanity check */ if (req->ifr_data == NULL) { PRINTM(MERROR, "woal_uap_operation_ctrl corrupt data\n"); ret = -EFAULT; goto done; } if (copy_from_user(¶m, req->ifr_data, sizeof(uap_oper_para_hdr))) { PRINTM(MERROR, "Copy from user failed\n"); ret = -EFAULT; goto done; } ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); if (ioctl_req == NULL) { ret = MLAN_STATUS_FAILURE; goto done; } bss = (mlan_ds_bss *)ioctl_req->pbuf; bss->sub_command = MLAN_OID_UAP_OPER_CTRL; ioctl_req->req_id = MLAN_IOCTL_BSS; if (copy_from_user(&uap_oper, req->ifr_data + sizeof(uap_oper_para_hdr), sizeof(mlan_uap_oper_ctrl))) { PRINTM(MERROR, "Copy from user failed\n"); ret = -EFAULT; goto done; } if (!param.action) { /* Get uap operation control parameters from FW */ ioctl_req->action = MLAN_ACT_GET; } else { /* Set uap operation control configurations */ ioctl_req->action = MLAN_ACT_SET; bss->param.ap_oper_ctrl.ctrl_value = uap_oper.ctrl_value; if (uap_oper.ctrl_value == 2) bss->param.ap_oper_ctrl.chan_opt = uap_oper.chan_opt; if (uap_oper.chan_opt == 3) { bss->param.ap_oper_ctrl.band_cfg = uap_oper.band_cfg; bss->param.ap_oper_ctrl.channel = uap_oper.channel; } } status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto done; } /* Copy to user */ if (copy_to_user(req->ifr_data + sizeof(uap_oper_para_hdr), &bss->param.ap_oper_ctrl, sizeof(mlan_uap_oper_ctrl))) { PRINTM(MERROR, "Copy to user failed!\n"); ret = -EFAULT; goto done; } done: if (status != MLAN_STATUS_PENDING) kfree(ioctl_req); 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; #ifdef SDIO case UAP_SDCMD52_RW: ret = woal_uap_sdcmd52_rw(dev, req); break; #endif case UAP_SNMP_MIB: ret = woal_uap_snmp_mib(dev, req); break; case UAP_DFS_TESTING: ret = woal_uap_dfs_testing(dev, req); break; case UAP_CHAN_SWITCH_COUNT_CFG: ret = woal_uap_chan_switch_count_cfg(dev, req); break; case UAP_DOMAIN_INFO: ret = woal_uap_domain_info(dev, req); break; case UAP_TX_BF_CFG: ret = woal_uap_tx_bf_cfg(dev, req); break; case UAP_HT_TX_CFG: ret = woal_uap_ht_tx_cfg(dev, req); break; case UAP_VHT_CFG: ret = woal_uap_vht_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; case UAP_HT_STREAM_CFG: ret = woal_uap_htstream_cfg(dev, req); break; case UAP_DFS_REPEATER_MODE: ret = woal_uap_dfs_repeater(dev, req); break; case UAP_CAC_TIMER_STATUS: ret = woal_uap_cac_timer_status(dev, req); break; case UAP_SKIP_CAC: ret = woal_uap_skip_cac(dev, req); break; case UAP_OPERATION_CTRL: ret = woal_uap_operation_ctrl(dev, req); break; case UAP_BAND_STEER: ret = woal_uap_band_steer(dev, req); break; case UAP_BEACON_STUCK_DETECT: ret = woal_uap_beacon_stuck(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; mlan_status status = MLAN_STATUS_SUCCESS; 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: " MACSTR ", reason=%d\n", MAC2STR(deauth_param.mac_addr), 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; moal_memcpy_ext(priv->phandle, &bss->param.deauth_param, &deauth_param, sizeof(mlan_deauth_param), sizeof(bss->param.deauth_param)); status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { 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 (status != MLAN_STATUS_PENDING) 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; mlan_status status = MLAN_STATUS_SUCCESS; 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]; status = woal_request_ioctl(priv, mreq, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) ret = -EFAULT; if (status != MLAN_STATUS_PENDING) 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 report mic error 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_report_mic_ioctl(struct net_device *dev, struct ifreq *req) { moal_private *priv = (moal_private *)netdev_priv(dev); mlan_ioctl_req *ioctl_req = NULL; mlan_ds_sec_cfg *sec = NULL; t_u8 mac_addr[MLAN_MAC_ADDR_LENGTH]; int ret = 0; mlan_status status = MLAN_STATUS_SUCCESS; ENTER(); memset(mac_addr, 0, MLAN_MAC_ADDR_LENGTH); /* Sanity check */ if (req->ifr_data == NULL) { PRINTM(MERROR, "uap_report_mic_ioctl() corrupt data\n"); ret = -EFAULT; goto done; } if (copy_from_user(mac_addr, req->ifr_data, MLAN_MAC_ADDR_LENGTH)) { PRINTM(MERROR, "Copy from user failed\n"); ret = -EFAULT; goto done; } PRINTM(MINFO, "ioctl report mic err station: " MACSTR "\n", MAC2STR(mac_addr)); ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg)); if (ioctl_req == NULL) { ret = -ENOMEM; goto done; } sec = (mlan_ds_sec_cfg *)ioctl_req->pbuf; sec->sub_command = MLAN_OID_SEC_CFG_REPORT_MIC_ERR; ioctl_req->req_id = MLAN_IOCTL_SEC_CFG; ioctl_req->action = MLAN_ACT_SET; moal_memcpy_ext(priv->phandle, sec->param.sta_mac, mac_addr, MLAN_MAC_ADDR_LENGTH, sizeof(sec->param.sta_mac)); status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto done; } done: if (status != MLAN_STATUS_PENDING) kfree(ioctl_req); LEAVE(); return ret; } /** * @brief uap set key 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_set_key_ioctl(struct net_device *dev, struct ifreq *req) { moal_private *priv = (moal_private *)netdev_priv(dev); mlan_ioctl_req *ioctl_req = NULL; mlan_ds_sec_cfg *sec = NULL; encrypt_key key; int ret = 0; t_u8 bcast_addr[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; mlan_status status = MLAN_STATUS_SUCCESS; ENTER(); memset(&key, 0, sizeof(encrypt_key)); /* Sanity check */ if (req->ifr_data == NULL) { PRINTM(MERROR, "uap_set_key_ioctl() corrupt data\n"); ret = -EFAULT; goto done; } if (copy_from_user(&key, req->ifr_data, sizeof(encrypt_key))) { PRINTM(MERROR, "Copy from user failed\n"); ret = -EFAULT; goto done; } PRINTM(MIOCTL, "ioctl report set key: " MACSTR " key_index=%d, key_len=%d \n", MAC2STR(key.mac_addr), (int)key.key_index, (int)key.key_len); if ((key.key_len > MLAN_MAX_KEY_LENGTH) || (key.key_index > 3)) { ret = -EINVAL; goto done; } ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg)); if (ioctl_req == NULL) { ret = -ENOMEM; goto done; } sec = (mlan_ds_sec_cfg *)ioctl_req->pbuf; sec->sub_command = MLAN_OID_SEC_CFG_ENCRYPT_KEY, ioctl_req->req_id = MLAN_IOCTL_SEC_CFG; ioctl_req->action = MLAN_ACT_SET; moal_memcpy_ext(priv->phandle, sec->param.encrypt_key.mac_addr, key.mac_addr, MLAN_MAC_ADDR_LENGTH, sizeof(sec->param.encrypt_key.mac_addr)); sec->param.encrypt_key.key_index = key.key_index; sec->param.encrypt_key.key_len = key.key_len; moal_memcpy_ext(priv->phandle, sec->param.encrypt_key.key_material, key.key_material, key.key_len, sizeof(sec->param.encrypt_key.key_material)); if (0 == memcmp(sec->param.encrypt_key.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; status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto done; } done: if (status != MLAN_STATUS_PENDING) kfree(ioctl_req); LEAVE(); return ret; } /** * @brief Set/Get uap power mode * * @param priv A pointer to moal_private structure * @param action Action set or get * @param ps_mgmt A pointer to mlan_ds_ps_mgmt structure * * @return MLAN_STATUS_SUCCESS -- success, otherwise fail */ int woal_set_get_uap_power_mode(moal_private *priv, t_u32 action, mlan_ds_ps_mgmt *ps_mgmt) { mlan_ioctl_req *ioctl_req = NULL; mlan_ds_pm_cfg *pm_cfg = NULL; mlan_status status = MLAN_STATUS_SUCCESS; ENTER(); if (!ps_mgmt) { LEAVE(); return MLAN_STATUS_FAILURE; } ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_pm_cfg)); if (ioctl_req == NULL) { LEAVE(); return MLAN_STATUS_FAILURE; } 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; ioctl_req->action = action; if (action == MLAN_ACT_SET) moal_memcpy_ext(priv->phandle, &pm_cfg->param.ps_mgmt, ps_mgmt, sizeof(mlan_ds_ps_mgmt), sizeof(pm_cfg->param.ps_mgmt)); status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); if (status == MLAN_STATUS_SUCCESS) { if (action == MLAN_ACT_GET) moal_memcpy_ext(priv->phandle, ps_mgmt, &pm_cfg->param.ps_mgmt, sizeof(mlan_ds_ps_mgmt), sizeof(mlan_ds_ps_mgmt)); } if (status != MLAN_STATUS_PENDING) kfree(ioctl_req); LEAVE(); return status; } /** * @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; mlan_status status = MLAN_STATUS_SUCCESS; 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; } if (priv->bss_type != MLAN_BSS_TYPE_UAP) { PRINTM(MERROR, "Invlaid BSS_TYPE for UAP power mode command\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; moal_memcpy_ext(priv->phandle, &pm_cfg->param.ps_mgmt, &ps_mgmt, sizeof(mlan_ds_ps_mgmt), sizeof(pm_cfg->param.ps_mgmt)); } else { ioctl_req->action = MLAN_ACT_GET; } status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { 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 (status != MLAN_STATUS_PENDING) 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; mlan_status status = MLAN_STATUS_SUCCESS; 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; } } status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { 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 (status != MLAN_STATUS_PENDING) 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; mlan_status status = MLAN_STATUS_SUCCESS; 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; status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { 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, ioctl_req->data_read_written)) { PRINTM(MERROR, "Copy to user failed!\n"); ret = -EFAULT; goto done; } } done: if (status != MLAN_STATUS_PENDING) 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}; mlan_status status = MLAN_STATUS_SUCCESS; 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; moal_memcpy_ext(priv->phandle, sec->param.encrypt_key.mac_addr, key_msg->mac_addr, ETH_ALEN, sizeof(sec->param.encrypt_key.mac_addr)); 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; moal_memcpy_ext(priv->phandle, sec->param.encrypt_key.key_material, key_msg->key, sec->param.encrypt_key.key_len, sizeof(sec->param.encrypt_key.key_material)); status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) ret = -EFAULT; done: if (status != MLAN_STATUS_PENDING) 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 (status != MLAN_STATUS_PENDING) 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; mlan_status status = MLAN_STATUS_SUCCESS; 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) { moal_memcpy_ext(priv->phandle, misc->param.gen_ie.ie_data, wapi_psk_ie, misc->param.gen_ie.len, sizeof(misc->param.gen_ie.ie_data)); } else if (msg->msg[0] & WAPI_MODE_CERT) { moal_memcpy_ext(priv->phandle, misc->param.gen_ie.ie_data, wapi_cert_ie, misc->param.gen_ie.len, sizeof(misc->param.gen_ie.ie_data)); } 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; } status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto done; } woal_enable_wapi(priv, MTRUE); done: if ((status != MLAN_STATUS_PENDING) && 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 */ static 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; } moal_memcpy_ext(priv->phandle, sys_cfg, &info->param.bss_config, sizeof(mlan_uap_bss_param), sizeof(mlan_uap_bss_param)); done: if (status != MLAN_STATUS_PENDING) kfree(req); LEAVE(); return status; } /** * @brief Set uap httxcfg * * @param priv A pointer to moal_private structure * @param band_cfg Band cfg * @param en Enabled/Disabled * * @return 0 --success, otherwise fail */ int woal_set_uap_ht_tx_cfg(moal_private *priv, Band_Config_t bandcfg, t_u16 ht_cap, t_u8 en) { int ret = 0; mlan_ds_11n_cfg *cfg_11n = NULL; mlan_ioctl_req *ioctl_req = NULL; mlan_status status = MLAN_STATUS_SUCCESS; ENTER(); ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); if (ioctl_req == NULL) { ret = -ENOMEM; goto done; } cfg_11n = (mlan_ds_11n_cfg *)ioctl_req->pbuf; cfg_11n->sub_command = MLAN_OID_11N_CFG_TX; ioctl_req->req_id = MLAN_IOCTL_11N_CFG; /* Set HT Tx configurations */ if (bandcfg.chanBand == BAND_2GHZ) { if (en) cfg_11n->param.tx_cfg.httxcap = ht_cap; else cfg_11n->param.tx_cfg.httxcap = 0; cfg_11n->param.tx_cfg.misc_cfg = BAND_SELECT_BG; } else if (bandcfg.chanBand == BAND_5GHZ) { if (en) cfg_11n->param.tx_cfg.httxcap = ht_cap; else cfg_11n->param.tx_cfg.httxcap = 0; cfg_11n->param.tx_cfg.misc_cfg = BAND_SELECT_A; } PRINTM(MCMND, "SET: httxcap=0x%x band:0x%x\n", cfg_11n->param.tx_cfg.httxcap, cfg_11n->param.tx_cfg.misc_cfg); /* Update 11n tx parameters in MLAN */ ioctl_req->action = MLAN_ACT_SET; status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto done; } done: if (status != MLAN_STATUS_PENDING) kfree(ioctl_req); LEAVE(); return ret; } /** * @brief Set 11n status based on the configured security mode * * @param priv A pointer to moal_private structure * @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(moal_private *priv, mlan_uap_bss_param *sys_cfg, t_u8 action) { mlan_status status = MLAN_STATUS_SUCCESS; mlan_fw_info fw_info; ENTER(); memset(&fw_info, 0, sizeof(mlan_fw_info)); 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) { woal_request_get_fw_info(priv, MOAL_IOCTL_WAIT, &fw_info); sys_cfg->supported_mcs_set[0] = 0xFF; if (sys_cfg->bandcfg.chan2Offset) sys_cfg->supported_mcs_set[4] = 0x01; else sys_cfg->supported_mcs_set[4] = 0x0; if (fw_info.usr_dev_mcs_support == HT_STREAM_MODE_2X2) sys_cfg->supported_mcs_set[1] = 0xFF; else sys_cfg->supported_mcs_set[1] = 0; } done: LEAVE(); return status; } #define VHT_CAP_11AC_MASK 0x007fffff /** * @brief enable/disable 11AC * * @param priv A pointer to moal_private structure * @param action MLAN_ACT_DISABLE or MLAN_ACT_ENABLE * @param vht20_40 Enable VHT 20 MHz or 40 MHz band * @param vhtcap_ie A pointer to vht capability IE * * @return 0--success, otherwise failure */ int woal_uap_set_11ac_status(moal_private *priv, t_u8 action, t_u8 vht20_40, IEEEtypes_VHTCap_t *vhtcap_ie) { mlan_ioctl_req *req = NULL; mlan_ds_11ac_cfg *cfg_11ac = NULL; mlan_fw_info fw_info; int ret = 0; mlan_status status = MLAN_STATUS_SUCCESS; ENTER(); memset(&fw_info, 0, sizeof(mlan_fw_info)); woal_request_get_fw_info(priv, MOAL_IOCTL_WAIT, &fw_info); req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11ac_cfg)); if (req == NULL) { ret = -ENOMEM; goto done; } cfg_11ac = (mlan_ds_11ac_cfg *)req->pbuf; cfg_11ac->sub_command = MLAN_OID_11AC_VHT_CFG; req->req_id = MLAN_IOCTL_11AC_CFG; req->action = MLAN_ACT_SET; cfg_11ac->param.vht_cfg.band = BAND_SELECT_A; cfg_11ac->param.vht_cfg.txrx = MLAN_RADIO_TXRX; /* * p2p GO (negotiation or auto GO) cases, wpa_supplicant will download * invalid vht capability with value 0 in beacon parameters, so for p2p * GO case (vht_cap_info = 0), driver will use hardware 11ac vht * capability value instead of up layer value. */ if (vhtcap_ie && vhtcap_ie->vht_cap.vht_cap_info != 0) { cfg_11ac->param.vht_cfg.vht_cap_info = woal_le32_to_cpu(vhtcap_ie->vht_cap.vht_cap_info); /** todo mcs configuration */ } else { cfg_11ac->param.vht_cfg.vht_cap_info = fw_info.usr_dot_11ac_dev_cap_a; } if (action == MLAN_ACT_DISABLE) { cfg_11ac->param.vht_cfg.bwcfg = MFALSE; cfg_11ac->param.vht_cfg.vht_cap_info &= ~VHT_CAP_11AC_MASK; cfg_11ac->param.vht_cfg.vht_rx_mcs = cfg_11ac->param.vht_cfg.vht_tx_mcs = 0xffff; cfg_11ac->param.vht_cfg.skip_usr_11ac_mcs_cfg = MTRUE; } else { if (vht20_40) cfg_11ac->param.vht_cfg.bwcfg = MFALSE; else cfg_11ac->param.vht_cfg.bwcfg = MTRUE; cfg_11ac->param.vht_cfg.vht_cap_info &= ~DEFALUT_11AC_CAP_BEAMFORMING_RESET_MASK; cfg_11ac->param.vht_cfg.vht_tx_mcs = fw_info.usr_dot_11ac_mcs_support >> 16; cfg_11ac->param.vht_cfg.vht_rx_mcs = fw_info.usr_dot_11ac_mcs_support & 0xffff; cfg_11ac->param.vht_cfg.skip_usr_11ac_mcs_cfg = MTRUE; } PRINTM(MCMND, "Uap:11ac=%d vht_cap_info=0x%x, vht_tx_mcs=0x%x, vht_rx_mcs=0x%x\n", action, cfg_11ac->param.vht_cfg.vht_cap_info, cfg_11ac->param.vht_cfg.vht_tx_mcs, cfg_11ac->param.vht_cfg.vht_rx_mcs); status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto done; } done: if (status != MLAN_STATUS_PENDING) kfree(req); LEAVE(); return ret; } /** * @brief Get/Set 11ax cfg * * @param priv A pointer to moal_private structure * @param action MLAN_ACT_SET or MLAN_ACT_GET * @param he_cfg a pointer to mlan_ds_11ax_he_cfg * * @return 0--success, otherwise failure */ int woal_11ax_cfg(moal_private *priv, t_u8 action, mlan_ds_11ax_he_cfg *he_cfg) { int ret = 0; mlan_status status = MLAN_STATUS_SUCCESS; mlan_ioctl_req *req = NULL; mlan_ds_11ax_cfg *cfg_11ax = NULL; req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11ax_cfg)); if (req == NULL) { ret = -ENOMEM; goto done; } cfg_11ax = (mlan_ds_11ax_cfg *)req->pbuf; cfg_11ax->sub_command = MLAN_OID_11AX_HE_CFG; req->req_id = MLAN_IOCTL_11AX_CFG; req->action = action; moal_memcpy_ext(priv->phandle, &cfg_11ax->param.he_cfg, he_cfg, sizeof(mlan_ds_11ax_he_cfg), sizeof(mlan_ds_11ax_he_cfg)); status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto done; } moal_memcpy_ext(priv->phandle, he_cfg, &cfg_11ax->param.he_cfg, sizeof(mlan_ds_11ax_he_cfg), sizeof(mlan_ds_11ax_he_cfg)); done: if (status != MLAN_STATUS_PENDING) kfree(req); LEAVE(); return ret; } /** * @brief enable/disable 11AX * * @param priv A pointer to moal_private structure * @param action MLAN_ACT_DISABLE or MLAN_ACT_ENABLE * @param band band config * @param hecap_ie * * @return 0--success, otherwise failure */ int woal_uap_set_11ax_status(moal_private *priv, t_u8 action, t_u8 band, IEEEtypes_HECap_t *hecap_ie) { mlan_fw_info fw_info; int ret = 0; mlan_ds_11ax_he_cfg he_cfg; ENTER(); memset(&fw_info, 0, sizeof(mlan_fw_info)); woal_request_get_fw_info(priv, MOAL_IOCTL_WAIT, &fw_info); if ((band == BAND_5GHZ && !(fw_info.fw_bands & BAND_AAX)) || (band == BAND_2GHZ && !(fw_info.fw_bands & BAND_GAX))) { PRINTM(MERROR, "fw doesn't support 11ax\n"); ret = -EFAULT; goto done; } memset(&he_cfg, 0, sizeof(he_cfg)); if (band == BAND_5GHZ) he_cfg.band = MBIT(1); else if (band == BAND_2GHZ) he_cfg.band = MBIT(0); else { PRINTM(MERROR, "Invalid band!\n"); ret = -EFAULT; goto done; } if (woal_11ax_cfg(priv, MLAN_ACT_GET, &he_cfg)) { PRINTM(MERROR, "Fail to get 11ax cfg!\n"); ret = -EFAULT; goto done; } if (hecap_ie) { DBG_HEXDUMP(MCMD_D, "hecap_ie", (t_u8 *)hecap_ie, hecap_ie->ieee_hdr.len + sizeof(IEEEtypes_Header_t)); he_cfg.he_cap.id = hecap_ie->ieee_hdr.element_id; he_cfg.he_cap.len = hecap_ie->ieee_hdr.len; moal_memcpy_ext(priv->phandle, &he_cfg.he_cap.ext_id, &hecap_ie->ext_id, he_cfg.he_cap.len, he_cfg.he_cap.len); } if (action == MLAN_ACT_DISABLE) { if (he_cfg.he_cap.len && (he_cfg.he_cap.ext_id == HE_CAPABILITY)) { memset(he_cfg.he_cap.he_txrx_mcs_support, 0xff, sizeof(he_cfg.he_cap.he_txrx_mcs_support)); } else { PRINTM(MCMND, "11ax already disabled\n"); goto done; } } DBG_HEXDUMP(MCMD_D, "HE_CFG ", (t_u8 *)&he_cfg, sizeof(he_cfg)); ret = woal_11ax_cfg(priv, MLAN_ACT_SET, &he_cfg); done: LEAVE(); return ret; } /** * @brief Parse AP configuration from ASCII string * * @param priv A pointer to moal_private structure * @param ap_cfg A pointer to mlan_uap_bss_param structure * @param buf A pointer to user data * * @return 0 --success, otherwise fail */ static int woal_uap_ap_cfg_parse_data(moal_private *priv, mlan_uap_bss_param *ap_cfg, char *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; char *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, MIN(MLAN_MAX_SSID_LENGTH - 1, 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); moal_memcpy_ext( priv->phandle, ap_cfg->wep_cfg.key0.key, value, strlen(value), sizeof(ap_cfg->wep_cfg.key0.key)); 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); moal_memcpy_ext(priv->phandle, ap_cfg->wpa_cfg.passphrase, value, strlen(value), sizeof(ap_cfg->wpa_cfg.passphrase)); 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 char buf[MAX_BUF_LEN]; mlan_uap_bss_param *sys_config = NULL; 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; } sys_config = kzalloc(sizeof(mlan_uap_bss_param), GFP_ATOMIC); if (!sys_config) { PRINTM(MERROR, "Fail to alloc memory for mlan_uap_bss_param\n"); ret = -EFAULT; goto done; } memset(buf, 0, MAX_BUF_LEN); moal_memcpy_ext(priv->phandle, buf, data, len, sizeof(buf) - 1); /* 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; ret = woal_uap_ap_cfg_parse_data(priv, sys_config, buf); if (ret) goto done; /* If BSS already started stop it first and restart * after changing the setting */ if (priv->bss_started == MTRUE) { ret = woal_uap_bss_ctrl(priv, MOAL_IOCTL_WAIT, UAP_BSS_STOP); if (ret) 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(priv, sys_config, MLAN_ACT_DISABLE)) { ret = -EFAULT; goto done; } } else { if (MLAN_STATUS_SUCCESS != woal_uap_set_11n_status(priv, 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: kfree(sys_config); LEAVE(); return ret; } /** * @brief Set/Get ap scan channel list * * @param priv A pointer to moal_private structure * @param action MLAN_ACT_SET or MLAN_ACT_GET * @param scan_channels A pointer to mlan_uap_scan_channels structure * * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE */ static mlan_status woal_set_get_ap_scan_channels(moal_private *priv, t_u16 action, mlan_uap_scan_channels *scan_channels) { 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_SCAN_CHANNELS; req->req_id = MLAN_IOCTL_BSS; req->action = action; moal_memcpy_ext(priv->phandle, &bss->param.ap_scan_channels, scan_channels, sizeof(mlan_uap_scan_channels), sizeof(bss->param.ap_scan_channels)); ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); if (ret != MLAN_STATUS_SUCCESS) goto done; if (action == MLAN_ACT_GET) moal_memcpy_ext(priv->phandle, scan_channels, &bss->param.ap_scan_channels, sizeof(mlan_uap_scan_channels), sizeof(mlan_uap_scan_channels)); done: if (ret != MLAN_STATUS_PENDING) kfree(req); LEAVE(); return ret; } /** * @brief Set/Get uap channel * * @param priv A pointer to moal_private structure * @param action MLAN_ACT_SET or MLAN_ACT_GET * @param wait_option wait option * @param uap_channel A pointer to mlan_uap_channel structure * * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE */ mlan_status woal_set_get_ap_channel(moal_private *priv, t_u16 action, t_u8 wait_option, chan_band_info *uap_channel) { 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_CHANNEL; req->req_id = MLAN_IOCTL_BSS; req->action = action; moal_memcpy_ext(priv->phandle, &bss->param.ap_channel, uap_channel, sizeof(chan_band_info), sizeof(bss->param.ap_channel)); ret = woal_request_ioctl(priv, req, wait_option); if (ret != MLAN_STATUS_SUCCESS) goto done; if (action == MLAN_ACT_GET) moal_memcpy_ext(priv->phandle, uap_channel, &bss->param.ap_channel, sizeof(chan_band_info), sizeof(chan_band_info)); done: if (ret != MLAN_STATUS_PENDING) kfree(req); LEAVE(); return ret; } /** * @brief start ACS scan * * @param priv A pointer to moal_private structure * @param action MLAN_ACT_SET or MLAN_ACT_GET * * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE */ static mlan_status woal_start_acs_scan(moal_private *priv) { 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_ACS_SCAN; req->req_id = MLAN_IOCTL_BSS; req->action = MLAN_ACT_SET; ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); if (ret != MLAN_STATUS_SUCCESS) goto done; PRINTM(MIOCTL, "ACS scan done: bandcfg:[chanBand=0x%x chanWidth=0x%x chan2Offset=0x%x scanMode=0x%x], channel=%d\n", bss->param.ap_acs_scan.bandcfg.chanBand, bss->param.ap_acs_scan.bandcfg.chanWidth, bss->param.ap_acs_scan.bandcfg.chan2Offset, bss->param.ap_acs_scan.bandcfg.scanMode, bss->param.ap_acs_scan.chan); done: if (ret != MLAN_STATUS_PENDING) kfree(req); LEAVE(); return ret; } /** * @brie check if we need do ACS scan * * @param priv A pointer to moal_private structure * * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE */ static mlan_status woal_do_acs_check(moal_private *priv) { mlan_status ret = MLAN_STATUS_SUCCESS; mlan_uap_bss_param *sys_config = NULL; mlan_uap_scan_channels *scan_channels = NULL; chan_band_info uap_channel; ENTER(); sys_config = kzalloc(sizeof(mlan_uap_bss_param), GFP_ATOMIC); if (!sys_config) { PRINTM(MERROR, "Fail to alloc memory for mlan_uap_bss_param\n"); LEAVE(); return MLAN_STATUS_FAILURE; } if (MLAN_STATUS_SUCCESS != woal_set_get_sys_config(priv, MLAN_ACT_GET, MOAL_IOCTL_WAIT, sys_config)) { PRINTM(MERROR, "Fail to get sys config data\n"); kfree(sys_config); LEAVE(); return MLAN_STATUS_FAILURE; } if (!(sys_config->bandcfg.scanMode == SCAN_MODE_ACS)) { kfree(sys_config); LEAVE(); return ret; } scan_channels = kzalloc(sizeof(mlan_uap_scan_channels), GFP_ATOMIC); if (scan_channels == NULL) { PRINTM(MERROR, "Fail to alloc scan channels buffer\n"); kfree(sys_config); LEAVE(); return MLAN_STATUS_FAILURE; } scan_channels->remove_nop_channel = MTRUE; if (MLAN_STATUS_SUCCESS != woal_set_get_ap_scan_channels(priv, MLAN_ACT_GET, scan_channels)) { PRINTM(MERROR, "Fail to get scan channels\n"); goto done; } if (scan_channels->num_remvoed_channel && scan_channels->num_of_chan) { scan_channels->remove_nop_channel = 0; /** set back new channel list after remove nop channels */ if (MLAN_STATUS_SUCCESS != woal_set_get_ap_scan_channels(priv, MLAN_ACT_SET, scan_channels)) { PRINTM(MERROR, "Fail to get scan channels\n"); goto done; } } if (scan_channels->num_of_chan) ret = woal_start_acs_scan(priv); else ret = MLAN_STATUS_FAILURE; /** set to default channel 6 when 5G ACS is configured */ if ((ret != MLAN_STATUS_SUCCESS) && (sys_config->bandcfg.chanBand == BAND_5GHZ)) { memset(&uap_channel, 0, sizeof(uap_channel)); uap_channel.bandcfg.chanBand = DEFAULT_UAP_BAND; uap_channel.channel = DEFAULT_UAP_CHANNEL; ret = woal_set_get_ap_channel(priv, MLAN_ACT_SET, MOAL_IOCTL_WAIT, &uap_channel); } done: kfree(scan_channels); kfree(sys_config); 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; mlan_status status = MLAN_STATUS_SUCCESS; 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 if (!priv->uap_host_based #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) || moal_extflg_isset(priv->phandle, EXT_DFS_OFFLOAD) #endif ) { woal_do_acs_check(priv); /* about to start bss: issue channel check */ status = woal_11h_channel_check_ioctl(priv, MOAL_IOCTL_WAIT); if (status) { PRINTM(MMSG, "11h channel check fails\n"); status = MLAN_STATUS_FAILURE; ret = -1; goto done; } } bss->sub_command = MLAN_OID_BSS_START; if (priv->uap_host_based) { bss->param.host_based |= UAP_FLAG_HOST_BASED; #ifdef UAP_CFG80211 #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) if (moal_extflg_isset(priv->phandle, EXT_HOST_MLME)) bss->param.host_based |= UAP_FLAG_HOST_MLME; #endif #endif } break; case UAP_BSS_STOP: if (priv->bss_started == MFALSE) { PRINTM(MWARN, "Warning: BSS already stopped!\n"); /* This is a situation where CAC it started and BSS * start is dealyed and before CAC timer expires BSS * stop is triggered. * * Do not skip sending the BSS_STOP command since there * are many routines triggered on BSS_STOP command * response. */ woal_cancel_cac_block(priv); } 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; status = woal_request_ioctl(priv, req, wait_option); if (status == MLAN_STATUS_FAILURE) { ret = -EFAULT; goto done; } if (data == UAP_BSS_STOP || data == UAP_BSS_RESET) { priv->bss_started = MFALSE; woal_stop_queue(priv->netdev); if (netif_carrier_ok(priv->netdev)) netif_carrier_off(priv->netdev); if (data == UAP_BSS_RESET) woal_request_set_mac_address(priv, wait_option); woal_flush_tcp_sess_queue(priv); } done: if (status != MLAN_STATUS_PENDING) 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 */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0) int woal_uap_do_ioctl(struct net_device *dev, struct ifreq *req, void __user *data, int cmd) #else int woal_uap_do_ioctl(struct net_device *dev, struct ifreq *req, int cmd) #endif { int ret = 0; ENTER(); #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0) if (in_compat_syscall()) /* not implemented yet */ return -EOPNOTSUPP; #endif switch (cmd) { case WOAL_ANDROID_DEF_CMD: /** android default ioctl ID is SIOCDEVPRIVATE + 1 */ ret = woal_android_priv_cmd(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_REPORT_MIC_ERR: ret = woal_uap_report_mic_ioctl(dev, req); break; case UAP_SET_KEY: ret = woal_uap_set_key_ioctl(dev, req); break; case UAPHOSTPKTINJECT: ret = woal_send_host_packet(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; } #ifdef CONFIG_PROC_FS /** * @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_IOCTL_WAIT); if (status == MLAN_STATUS_SUCCESS) { PRINTM(MINFO, "MOAL UAP VERSION: %s\n", info->param.ver_ext.version_str); snprintf(version, max_len, priv->phandle->driver_version, info->param.ver_ext.version_str); } if (status != MLAN_STATUS_PENDING) kfree(req); LEAVE(); return; } #endif /** * @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) moal_memcpy_ext(priv->phandle, ustats, &info->param.ustats, sizeof(mlan_ds_uap_stats), 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 (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 ap_wmm_para A pointer to wmm_parameter_t structure * * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE */ mlan_status woal_set_get_ap_wmm_para(moal_private *priv, t_u16 action, wmm_parameter_t *ap_wmm_para) { 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_CFG_WMM_PARAM; req->req_id = MLAN_IOCTL_BSS; req->action = action; if (action == MLAN_ACT_SET) moal_memcpy_ext(priv->phandle, &bss->param.ap_wmm_para, ap_wmm_para, sizeof(wmm_parameter_t), sizeof(bss->param.ap_wmm_para)); ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); if (ret != MLAN_STATUS_SUCCESS) goto done; if (bss->param.ap_wmm_para.reserved != MLAN_STATUS_COMPLETE) { ret = MLAN_STATUS_FAILURE; goto done; } if (action == MLAN_ACT_GET) moal_memcpy_ext(priv->phandle, ap_wmm_para, &bss->param.ap_wmm_para, sizeof(wmm_parameter_t), sizeof(wmm_parameter_t)); done: if (ret != MLAN_STATUS_PENDING) kfree(req); LEAVE(); return ret; } /** * @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) moal_memcpy_ext(priv->phandle, &bss->param.bss_config, sys_cfg, sizeof(mlan_uap_bss_param), sizeof(bss->param.bss_config)); ret = woal_request_ioctl(priv, req, wait_option); if (ret != MLAN_STATUS_SUCCESS) goto done; if (action == MLAN_ACT_GET) moal_memcpy_ext(priv->phandle, sys_cfg, &bss->param.bss_config, sizeof(mlan_uap_bss_param), sizeof(mlan_uap_bss_param)); done: if (ret != MLAN_STATUS_PENDING) 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(); }