mwifiex/mxm_wifiex/wlan_src/mlinux/moal_uap.c
Sherry Sun 8ffae47921 mxm_wifiex: update to mxm5x17283 release
changes:
1. WCSWREL-191: Fixed the error when loading module param from user config for SD8801
2. WCSWREL-186: Fixed the issue of mlanutl failing on kernel higher than L5.15
3. Fixed low throughput issue for WPA3 SAE
4. Added driver change for WLAN throughput improvement on 8997 SoC
5. Updated README to recommend not to use WEP/TKIP for all chipsets
6. WCSWREL-180: Fix P2P test fail on kernel higher than L5.12
7. WCSWREL-156: kernel_write/kernel_read not allowed by drivers for L5.10 kernel GKI buildou
8. Alternative for pm_qos_add_request/pm_qos_remove_request

Signed-off-by: Sherry Sun <sherry.sun@nxp.com>
Approved-by: Tian Yang <yang.tian@nxp.com>
2021-10-12 12:16:50 +08:00

4547 lines
118 KiB
C

/** @file moal_uap.c
*
* @brief This file contains the major functions in UAP
* driver.
*
*
* Copyright 2008-2021 NXP
*
* This software file (the File) is distributed by NXP
* under the terms of the GNU General Public License Version 2, June 1991
* (the License). You may use, redistribute and/or modify the File in
* accordance with the terms and conditions of the License, a copy of which
* is available by writing to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
* worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
*
* THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
* IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
* ARE EXPRESSLY DISCLAIMED. The License provides additional details about
* this warranty disclaimer.
*
*/
/********************************************************
Change log:
10/21/2008: initial version
********************************************************/
#include "moal_main.h"
#include "moal_uap.h"
#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(&param, 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(&param, 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, &param, 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(&param, 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(&param, req->ifr_data, sizeof(param))) {
PRINTM(MERROR, "Copy from user failed\n");
ret = -EFAULT;
goto done;
}
DBG_HEXDUMP(MCMD_D, "aggr_prio_tbl", (t_u8 *)&param, sizeof(param));
ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg));
if (ioctl_req == NULL) {
LEAVE();
return -ENOMEM;
}
cfg_11n = (mlan_ds_11n_cfg *)ioctl_req->pbuf;
cfg_11n->sub_command = MLAN_OID_11N_CFG_AGGR_PRIO_TBL;
ioctl_req->req_id = MLAN_IOCTL_11N_CFG;
if (!param.action) {
/* Get aggr_prio_tbl from MLAN */
ioctl_req->action = MLAN_ACT_GET;
} else {
/* Set aggr_prio_tbl in MLAN */
ioctl_req->action = MLAN_ACT_SET;
for (i = 0; i < MAX_NUM_TID; i++) {
cfg_11n->param.aggr_prio_tbl.ampdu[i] = param.ampdu[i];
cfg_11n->param.aggr_prio_tbl.amsdu[i] = param.amsdu[i];
}
}
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, &param, 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(&param, 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(&param, req->ifr_data, sizeof(param))) {
PRINTM(MERROR, "Copy from user failed\n");
ret = -EFAULT;
goto done;
}
DBG_HEXDUMP(MCMD_D, "addba_reject tbl", (t_u8 *)&param, sizeof(param));
ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg));
if (ioctl_req == NULL) {
LEAVE();
return -ENOMEM;
}
cfg_11n = (mlan_ds_11n_cfg *)ioctl_req->pbuf;
cfg_11n->sub_command = MLAN_OID_11N_CFG_ADDBA_REJECT;
ioctl_req->req_id = MLAN_IOCTL_11N_CFG;
if (!param.action) {
/* Get addba_reject tbl from MLAN */
ioctl_req->action = MLAN_ACT_GET;
} else {
/* Set addba_reject tbl in MLAN */
ioctl_req->action = MLAN_ACT_SET;
for (i = 0; i < MAX_NUM_TID; i++)
cfg_11n->param.addba_reject[i] = param.addba_reject[i];
}
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, &param, 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(&param, 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(&param, req->ifr_data, sizeof(param))) {
PRINTM(MERROR, "Copy from user failed\n");
ret = -EFAULT;
goto done;
}
DBG_HEXDUMP(MCMD_D, "deep_sleep_para", (t_u8 *)&param, sizeof(param));
ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_pm_cfg));
if (ioctl_req == NULL) {
LEAVE();
return -ENOMEM;
}
pm = (mlan_ds_pm_cfg *)ioctl_req->pbuf;
pm->sub_command = MLAN_OID_PM_CFG_DEEP_SLEEP;
ioctl_req->req_id = MLAN_IOCTL_PM_CFG;
if (!param.action) {
/* Get deep_sleep status from MLAN */
ioctl_req->action = MLAN_ACT_GET;
} else {
/* Set deep_sleep in MLAN */
ioctl_req->action = MLAN_ACT_SET;
if (param.deep_sleep == MTRUE) {
pm->param.auto_deep_sleep.auto_ds = DEEP_SLEEP_ON;
pm->param.auto_deep_sleep.idletime = param.idle_time;
} else {
pm->param.auto_deep_sleep.auto_ds = DEEP_SLEEP_OFF;
}
}
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, &param, 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(&param, 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(&param, 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 *)&param, 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, &param, 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(&param, 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(&param, 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 *)&param,
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, &param, 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(&param, 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(&param, req->ifr_data, sizeof(param))) {
PRINTM(MERROR, "Copy from user failed\n");
ret = -EFAULT;
goto done;
}
DBG_HEXDUMP(MCMD_D, "tx_data_pause_para", (t_u8 *)&param,
sizeof(param));
ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg));
if (ioctl_req == NULL) {
LEAVE();
return -ENOMEM;
}
misc = (mlan_ds_misc_cfg *)ioctl_req->pbuf;
misc->sub_command = MLAN_OID_MISC_TX_DATAPAUSE;
ioctl_req->req_id = MLAN_IOCTL_MISC_CFG;
if (!param.action) {
/* Get Tx data pause status from MLAN */
ioctl_req->action = MLAN_ACT_GET;
} else {
/* Set Tx data pause in MLAN */
ioctl_req->action = MLAN_ACT_SET;
misc->param.tx_datapause.tx_pause = param.txpause;
misc->param.tx_datapause.tx_buf_cnt = param.txbufcnt;
}
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, &param, 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(&param, 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(&param, 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, &param, sizeof(param))) {
PRINTM(MERROR, "Copy to user failed!\n");
ret = -EFAULT;
}
done:
LEAVE();
return ret;
}
#endif
/**
* @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(&param, 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(&param, req->ifr_data, sizeof(param))) {
PRINTM(MERROR, "Copy from user failed\n");
ret = -EFAULT;
goto done;
}
DBG_HEXDUMP(MCMD_D, "snmp_mib_para", (t_u8 *)&param, sizeof(param));
if (param.action) {
if (copy_from_user(value, req->ifr_data + sizeof(param),
MIN(param.oid_val_len,
MAX_SNMP_VALUE_SIZE))) {
PRINTM(MERROR, "Copy from user failed\n");
ret = -EFAULT;
goto done;
}
DBG_HEXDUMP(MCMD_D, "snmp_mib_para value", value,
MIN(param.oid_val_len, sizeof(t_u32)));
}
ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_snmp_mib));
if (ioctl_req == NULL) {
LEAVE();
return -ENOMEM;
}
snmp = (mlan_ds_snmp_mib *)ioctl_req->pbuf;
ioctl_req->req_id = MLAN_IOCTL_SNMP_MIB;
switch (param.oid) {
case OID_80211D_ENABLE:
snmp->sub_command = MLAN_OID_SNMP_MIB_DOT11D;
break;
case OID_80211H_ENABLE:
snmp->sub_command = MLAN_OID_SNMP_MIB_DOT11H;
break;
default:
PRINTM(MERROR, "%s: Unsupported SNMP_MIB OID (%d).\n", __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, &param, 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(&param, 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(&param, req->ifr_data, sizeof(param))) {
PRINTM(MERROR, "Copy from user failed\n");
ret = -EFAULT;
goto done;
}
DBG_HEXDUMP(MCMD_D, "domain_info_para", (t_u8 *)&param, sizeof(param));
if (param.action) {
/* get tlv header */
if (copy_from_user(tlv, req->ifr_data + sizeof(param),
TLV_HEADER_LEN)) {
PRINTM(MERROR, "Copy from user failed\n");
ret = -EFAULT;
goto done;
}
tlv_data_len = ((t_u16 *)(tlv))[1];
if ((TLV_HEADER_LEN + tlv_data_len) > (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, &param, 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(&param, 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(&param, req->ifr_data, sizeof(param))) {
PRINTM(MERROR, "Copy from user failed\n");
ret = -EFAULT;
goto done;
}
DBG_HEXDUMP(MCMD_D, "dfs_testing_para", (t_u8 *)&param, sizeof(param));
ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11h_cfg));
if (ioctl_req == NULL) {
LEAVE();
return -ENOMEM;
}
cfg11h = (mlan_ds_11h_cfg *)ioctl_req->pbuf;
ioctl_req->req_id = MLAN_IOCTL_11H_CFG;
cfg11h->sub_command = MLAN_OID_11H_DFS_TESTING;
if (!param.action) {
/* Get mib value from MLAN */
ioctl_req->action = MLAN_ACT_GET;
} else {
/* Set mib value to MLAN */
ioctl_req->action = MLAN_ACT_SET;
cfg11h->param.dfs_testing.usr_cac_period_msec =
param.usr_cac_period * 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, &param, 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(&param, 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(&param, 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 *)&param, sizeof(param));
ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11h_cfg));
if (ioctl_req == NULL) {
LEAVE();
return -ENOMEM;
}
cfg11h = (mlan_ds_11h_cfg *)ioctl_req->pbuf;
ioctl_req->req_id = MLAN_IOCTL_11H_CFG;
cfg11h->sub_command = MLAN_OID_11H_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, &param, 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(&param, 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(&param, 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(&param, 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(&param, 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(&param, 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(&param, 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(&param, 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, &param, 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(&param, 0, sizeof(dfs_repeater_mode));
/* Get user data */
if (copy_from_user(&param, 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, &param, 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 Set/Get skip CAC 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_skip_cac(struct net_device *dev, struct ifreq *req)
{
moal_private *priv = (moal_private *)netdev_priv(dev);
int ret = 0;
skip_cac_para param;
ENTER();
/* Sanity check */
if (req->ifr_data == NULL) {
PRINTM(MERROR, "skip_cac() corrupt data\n");
ret = -EFAULT;
goto done;
}
memset(&param, 0, sizeof(skip_cac_para));
/* Get user data */
if (copy_from_user(&param, req->ifr_data, sizeof(skip_cac_para))) {
PRINTM(MERROR, "Copy from user failed\n");
ret = -EFAULT;
goto done;
}
/* Currently default action is get */
if (param.action == 0) {
param.skip_cac = (t_u16)priv->skip_cac;
} else {
priv->skip_cac = param.skip_cac;
}
if (copy_to_user(req->ifr_data, &param, sizeof(skip_cac_para))) {
PRINTM(MERROR, "Copy to user failed\n");
ret = -EFAULT;
}
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(&param, 0, sizeof(cac_timer_status));
/* Get user data */
if (copy_from_user(&param, 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, &param, 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(&param, 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(&param, 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();
}