blob: 5cadd81d75c49f9dffa8f92b3393214564fac151 [file] [log] [blame]
/** @file moal_priv.c
*
* @brief This file contains standard ioctl functions
*
* Copyright (C) 2008-2009, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
* (the "License"). You may use, redistribute and/or modify this File in
* accordance with the terms and conditions of the License, a copy of which
* is available by writing to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
* worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
*
* THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
* IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
* ARE EXPRESSLY DISCLAIMED. The License provides additional details about
* this warranty disclaimer.
*
*/
/************************************************************************
Change log:
10/30/2008: initial version
************************************************************************/
#include "moal_main.h"
#include "moal_sdio.h"
/********************************************************
Local Variables
********************************************************/
/** Bands supported in Infra mode */
static t_u8 SupportedInfraBand[] = {
BAND_B, BAND_B | BAND_G, BAND_G,
BAND_GN, BAND_B | BAND_G | BAND_GN, BAND_G | BAND_GN,
BAND_A, BAND_B | BAND_A, BAND_B | BAND_G | BAND_A, BAND_G | BAND_A,
BAND_A | BAND_B | BAND_G | BAND_AN | BAND_GN,
BAND_A | BAND_G | BAND_AN | BAND_GN, BAND_A | BAND_AN,
};
/** Bands supported in Ad-Hoc mode */
static t_u8 SupportedAdhocBand[] = {
BAND_B, BAND_B | BAND_G, BAND_G,
BAND_GN, BAND_B | BAND_G | BAND_GN, BAND_G | BAND_GN,
BAND_A,
BAND_AN, BAND_A | BAND_AN,
};
/********************************************************
Global Variables
********************************************************/
/** Macro for maximum size of scan response buffer */
#define MAX_SCAN_RSP_BUF (16 * 1024)
/********************************************************
Local Functions
********************************************************/
/**
* @brief Handle get info resp
*
* @param priv Pointer to moal_private structure
* @param info Pointer to mlan_ds_get_info structure
*
* @return N/A
*/
void
woal_ioctl_get_info_resp(moal_private * priv, mlan_ds_get_info * info)
{
ENTER();
switch (info->sub_command) {
case MLAN_OID_GET_STATS:
priv->w_stats.discard.fragment = info->param.stats.fcs_error;
priv->w_stats.discard.retries = info->param.stats.retry;
priv->w_stats.discard.misc = info->param.stats.ack_failure;
break;
case MLAN_OID_GET_SIGNAL:
if (info->param.signal.selector & BCN_RSSI_AVG_MASK)
priv->w_stats.qual.level = info->param.signal.bcn_rssi_avg;
if (info->param.signal.selector & BCN_NF_AVG_MASK)
priv->w_stats.qual.noise = info->param.signal.bcn_nf_avg;
break;
default:
break;
}
LEAVE();
}
/**
* @brief Handle get BSS resp
*
* @param priv Pointer to moal_private structure
* @param bss Pointer to mlan_ds_bss structure
*
* @return N/A
*/
void
woal_ioctl_get_bss_resp(moal_private * priv, mlan_ds_bss * bss)
{
t_u32 mode = 0;
ENTER();
switch (bss->sub_command) {
case MLAN_OID_BSS_MODE:
if (bss->param.bss_mode == MLAN_BSS_MODE_INFRA)
mode = IW_MODE_INFRA;
else if (bss->param.bss_mode == MLAN_BSS_MODE_IBSS)
mode = IW_MODE_ADHOC;
else
mode = IW_MODE_AUTO;
priv->w_stats.status = mode;
break;
default:
break;
}
LEAVE();
return;
}
/**
* @brief Copy Rates
*
* @param dest A pointer to destination buffer
* @param pos The position for copy
* @param src A pointer to source buffer
* @param len Length of the source buffer
*
* @return Number of rates copied
*/
static inline int
woal_copy_rates(t_u8 * dest, int pos, t_u8 * src, int len)
{
int i;
for (i = 0; i < len && src[i]; i++, pos++) {
if (pos >= MLAN_SUPPORTED_RATES)
break;
dest[pos] = src[i];
}
return pos;
}
/**
* @brief Get Driver Version
*
* @param priv A pointer to moal_private structure
* @param req A pointer to ifreq structure
*
* @return 0 --success, otherwise fail
*/
static int
woal_get_driver_version(moal_private * priv, struct ifreq *req)
{
struct iwreq *wrq = (struct iwreq *) req;
int len;
char buf[MLAN_MAX_VER_STR_LEN];
ENTER();
woal_get_version(priv->phandle, buf, sizeof(buf) - 1);
len = strlen(buf);
if (wrq->u.data.pointer) {
if (copy_to_user(wrq->u.data.pointer, buf, len)) {
PRINTM(MERROR, "Copy to user failed\n");
LEAVE();
return -EFAULT;
}
wrq->u.data.length = len;
}
PRINTM(MINFO, "MOAL VERSION: %s\n", buf);
LEAVE();
return 0;
}
/**
* @brief Get extended driver version
*
* @param priv A pointer to moal_private structure
* @param ireq A pointer to ifreq structure
*
* @return 0 --success, otherwise fail
*/
static int
woal_get_driver_verext(moal_private * priv, struct ifreq *ireq)
{
struct iwreq *wrq = (struct iwreq *) ireq;
mlan_ds_get_info *info = NULL;
mlan_ioctl_req *req = NULL;
ENTER();
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_get_info));
if (req == NULL) {
LEAVE();
return -ENOMEM;
}
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;
if (!wrq->u.data.flags) {
info->param.ver_ext.version_str_sel =
*((int *) (wrq->u.name + SUBCMD_OFFSET));
} else {
if (copy_from_user
(&info->param.ver_ext.version_str_sel, wrq->u.data.pointer,
sizeof(info->param.ver_ext.version_str_sel))) {
PRINTM(MERROR, "Copy from user failed\n");
LEAVE();
return -EFAULT;
} else {
if (info->param.ver_ext.version_str_sel < 0) {
PRINTM(MERROR, "Invalid arguments!\n");
LEAVE();
return -EINVAL;
}
}
}
if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
LEAVE();
return -EFAULT;
}
if (wrq->u.data.pointer) {
if (copy_to_user(wrq->u.data.pointer, info->param.ver_ext.version_str,
strlen(info->param.ver_ext.version_str))) {
PRINTM(MERROR, "Copy to user failed\n");
LEAVE();
return -EFAULT;
}
wrq->u.data.length = strlen(info->param.ver_ext.version_str);
}
PRINTM(MINFO, "MOAL EXTENDED VERSION: %s\n",
info->param.ver_ext.version_str);
LEAVE();
return 0;
}
/**
* @brief Set Deep Sleep
*
* @param priv Pointer to the moal_private driver data struct
* @param wait_option wait option
* @param bdeep_sleep TRUE--enalbe deepsleep, FALSE--disable deepsleep
* @param idletime Idle time for optimized PS API
*
* @return 0 --success, otherwise fail
*/
int
woal_set_deep_sleep(moal_private * priv, t_u8 wait_option, BOOLEAN bdeep_sleep,
t_u16 idletime)
{
int ret = 0;
mlan_ioctl_req *req = NULL;
mlan_ds_pm_cfg *pm = NULL;
ENTER();
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_pm_cfg));
if (req == NULL) {
LEAVE();
return -ENOMEM;
}
pm = (mlan_ds_pm_cfg *) req->pbuf;
pm->sub_command = MLAN_OID_PM_CFG_DEEP_SLEEP;
req->req_id = MLAN_IOCTL_PM_CFG;
req->action = MLAN_ACT_SET;
if (bdeep_sleep == MTRUE) {
PRINTM(MCMND, "Deep Sleep: sleep\n");
pm->param.auto_deep_sleep.auto_ds = DEEP_SLEEP_ON;
if (idletime) {
pm->param.auto_deep_sleep.idletime = idletime;
}
if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, wait_option)) {
ret = -EFAULT;
goto done;
}
} else {
PRINTM(MCMND, "%lu : Deep Sleep: wakeup\n", jiffies);
pm->param.auto_deep_sleep.auto_ds = DEEP_SLEEP_OFF;
if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, wait_option)) {
ret = -EFAULT;
goto done;
}
}
done:
if (req)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief Performs warm reset
*
* @param priv A pointer to moal_private structure
*
* @return 0 --success, otherwise fail
*/
static int
woal_warm_reset(moal_private * priv)
{
int ret = 0;
int intf_num;
moal_handle *handle = priv->phandle;
mlan_ioctl_req *req = NULL;
mlan_ds_misc_cfg *misc = NULL;
mlan_bss_info bss_info;
ENTER();
/* Disable interfaces */
for (intf_num = 0; intf_num < handle->priv_num; intf_num++) {
netif_stop_queue(handle->priv[intf_num]->netdev);
netif_device_detach(handle->priv[intf_num]->netdev);
}
/* Exit deep sleep */
woal_set_deep_sleep(priv, MOAL_IOCTL_WAIT, MFALSE, 0);
memset(&bss_info, 0, sizeof(bss_info));
woal_get_bss_info(priv, MOAL_PROC_WAIT, &bss_info);
if (bss_info.is_hs_configured) {
if (MLAN_STATUS_SUCCESS != woal_hs_cfg_cancel(priv, MOAL_IOCTL_WAIT)) {
ret = -EFAULT;
goto done;
}
}
/* Disconnect from network */
for (intf_num = 0; intf_num < handle->priv_num; intf_num++) {
if (handle->priv[intf_num]->media_connected == MTRUE) {
woal_disconnect(handle->priv[intf_num], MOAL_IOCTL_WAIT, NULL);
}
}
/* Initialize private structures */
for (intf_num = 0; intf_num < handle->priv_num; intf_num++)
woal_init_priv(handle->priv[intf_num]);
#ifdef REASSOCIATION
/* Reset the reassoc timer and status */
handle->reassoc_on = MFALSE;
if (handle->is_reassoc_timer_set == MTRUE) {
woal_cancel_timer(&handle->reassoc_timer);
handle->is_reassoc_timer_set = MFALSE;
}
#endif
/* Restart the firmware */
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg));
if (req) {
misc = (mlan_ds_misc_cfg *) req->pbuf;
misc->sub_command = MLAN_OID_MISC_WARM_RESET;
req->req_id = MLAN_IOCTL_MISC_CFG;
req->action = MLAN_ACT_SET;
if (MLAN_STATUS_SUCCESS !=
woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
ret = -EFAULT;
kfree(req);
goto done;
}
kfree(req);
}
/* Enable interfaces */
for (intf_num = 0; intf_num < handle->priv_num; intf_num++) {
netif_device_attach(handle->priv[intf_num]->netdev);
netif_start_queue(handle->priv[intf_num]->netdev);
}
done:
LEAVE();
return ret;
}
/**
* @brief Get signal
*
* @param priv A pointer to moal_private structure
* @param wrq A pointer to iwreq structure
*
* @return 0 --success, otherwise fail
*/
static int
woal_get_signal(moal_private * priv, struct iwreq *wrq)
{
/** Input data size */
#define IN_DATA_SIZE 2
/** Output data size */
#define OUT_DATA_SIZE 12
int ret = 0;
int in_data[IN_DATA_SIZE];
int out_data[OUT_DATA_SIZE];
mlan_ds_get_signal signal;
int data_length = 0;
ENTER();
memset(in_data, 0, sizeof(in_data));
memset(out_data, 0, sizeof(out_data));
if (priv->media_connected == MFALSE) {
PRINTM(MERROR, "Can not get RSSI in disconnected state\n");
ret = -ENOTSUPP;
goto done;
}
if (wrq->u.data.length) {
if (copy_from_user
(in_data, wrq->u.data.pointer,
sizeof(int) * MIN(wrq->u.data.length, sizeof(in_data)))) {
PRINTM(MERROR, "Copy from user failed\n");
ret = -EFAULT;
goto done;
}
}
switch (wrq->u.data.length) {
case 0: /* No checking, get everything */
break;
case 2: /* Check subtype range */
if (in_data[1] < 1 || in_data[1] > 4) {
ret = -EINVAL;
goto done;
}
/* Fall through */
case 1: /* Check type range */
if (in_data[0] < 1 || in_data[0] > 3) {
ret = -EINVAL;
goto done;
}
break;
default:
ret = -EINVAL;
goto done;
}
memset(&signal, 0, sizeof(mlan_ds_get_signal));
if (MLAN_STATUS_SUCCESS !=
woal_get_signal_info(priv, MOAL_IOCTL_WAIT, &signal)) {
ret = -EFAULT;
goto done;
}
PRINTM(MINFO, "RSSI Beacon Last : %d\n", (int) signal.bcn_rssi_last);
PRINTM(MINFO, "RSSI Beacon Average: %d\n", (int) signal.bcn_rssi_avg);
PRINTM(MINFO, "RSSI Data Last : %d\n", (int) signal.data_rssi_last);
PRINTM(MINFO, "RSSI Data Average : %d\n", (int) signal.data_rssi_avg);
PRINTM(MINFO, "SNR Beacon Last : %d\n", (int) signal.bcn_snr_last);
PRINTM(MINFO, "SNR Beacon Average : %d\n", (int) signal.bcn_snr_avg);
PRINTM(MINFO, "SNR Data Last : %d\n", (int) signal.data_snr_last);
PRINTM(MINFO, "SNR Data Average : %d\n", (int) signal.data_snr_avg);
PRINTM(MINFO, "NF Beacon Last : %d\n", (int) signal.bcn_nf_last);
PRINTM(MINFO, "NF Beacon Average : %d\n", (int) signal.bcn_nf_avg);
PRINTM(MINFO, "NF Data Last : %d\n", (int) signal.data_nf_last);
PRINTM(MINFO, "NF Data Average : %d\n", (int) signal.data_nf_avg);
/* Check type */
switch (in_data[0]) {
case 0: /* Send everything */
out_data[data_length++] = signal.bcn_rssi_last;
out_data[data_length++] = signal.bcn_rssi_avg;
out_data[data_length++] = signal.data_rssi_last;
out_data[data_length++] = signal.data_rssi_avg;
out_data[data_length++] = signal.bcn_snr_last;
out_data[data_length++] = signal.bcn_snr_avg;
out_data[data_length++] = signal.data_snr_last;
out_data[data_length++] = signal.data_snr_avg;
out_data[data_length++] = signal.bcn_nf_last;
out_data[data_length++] = signal.bcn_nf_avg;
out_data[data_length++] = signal.data_nf_last;
out_data[data_length++] = signal.data_nf_avg;
break;
case 1: /* RSSI */
/* Check subtype */
switch (in_data[1]) {
case 0: /* Everything */
out_data[data_length++] = signal.bcn_rssi_last;
out_data[data_length++] = signal.bcn_rssi_avg;
out_data[data_length++] = signal.data_rssi_last;
out_data[data_length++] = signal.data_rssi_avg;
break;
case 1: /* bcn last */
out_data[data_length++] = signal.bcn_rssi_last;
break;
case 2: /* bcn avg */
out_data[data_length++] = signal.bcn_rssi_avg;
break;
case 3: /* data last */
out_data[data_length++] = signal.data_rssi_last;
break;
case 4: /* data avg */
out_data[data_length++] = signal.data_rssi_avg;
break;
default:
break;
}
break;
case 2: /* SNR */
/* Check subtype */
switch (in_data[1]) {
case 0: /* Everything */
out_data[data_length++] = signal.bcn_snr_last;
out_data[data_length++] = signal.bcn_snr_avg;
out_data[data_length++] = signal.data_snr_last;
out_data[data_length++] = signal.data_snr_avg;
break;
case 1: /* bcn last */
out_data[data_length++] = signal.bcn_snr_last;
break;
case 2: /* bcn avg */
out_data[data_length++] = signal.bcn_snr_avg;
break;
case 3: /* data last */
out_data[data_length++] = signal.data_snr_last;
break;
case 4: /* data avg */
out_data[data_length++] = signal.data_snr_avg;
break;
default:
break;
}
break;
case 3: /* NF */
/* Check subtype */
switch (in_data[1]) {
case 0: /* Everything */
out_data[data_length++] = signal.bcn_nf_last;
out_data[data_length++] = signal.bcn_nf_avg;
out_data[data_length++] = signal.data_nf_last;
out_data[data_length++] = signal.data_nf_avg;
break;
case 1: /* bcn last */
out_data[data_length++] = signal.bcn_nf_last;
break;
case 2: /* bcn avg */
out_data[data_length++] = signal.bcn_nf_avg;
break;
case 3: /* data last */
out_data[data_length++] = signal.data_nf_last;
break;
case 4: /* data avg */
out_data[data_length++] = signal.data_nf_avg;
break;
default:
break;
}
break;
default:
break;
}
wrq->u.data.length = data_length;
if (copy_to_user(wrq->u.data.pointer, out_data,
wrq->u.data.length * sizeof(out_data[0]))) {
PRINTM(MERROR, "Copy to user failed\n");
ret = -EFAULT;
goto done;
}
done:
LEAVE();
return ret;
}
/**
* @brief Get Deep Sleep
*
* @param priv Pointer to the moal_private driver data struct
* @param deep_sleep Pointer to return deep_sleep setting
*
* @return 0 --success, otherwise fail
*/
static int
woal_get_deep_sleep(moal_private * priv, t_u32 * deep_sleep)
{
int ret = 0;
mlan_ioctl_req *req = NULL;
mlan_ds_pm_cfg *pm = NULL;
ENTER();
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_pm_cfg));
if (req == NULL) {
LEAVE();
return -ENOMEM;
}
pm = (mlan_ds_pm_cfg *) req->pbuf;
pm->sub_command = MLAN_OID_PM_CFG_DEEP_SLEEP;
req->req_id = MLAN_IOCTL_PM_CFG;
req->action = MLAN_ACT_GET;
if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
ret = -EFAULT;
goto done;
}
*deep_sleep = pm->param.auto_deep_sleep.auto_ds;
done:
if (req)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief Get/Set DeepSleep mode
*
* @param priv Pointer to the moal_private driver data struct
* @param wreq A pointer to iwreq structure
*
* @return 0 --success, otherwise fail
*/
static int
woal_deep_sleep_ioctl(moal_private * priv, struct iwreq *wrq)
{
t_u32 deep_sleep = DEEP_SLEEP_OFF;
static t_u16 idletime = DEEP_SLEEP_ILDE_TIME;
t_u32 data[2];
ENTER();
if (wrq->u.data.length == 1 || wrq->u.data.length == 2) {
if (copy_from_user
(&data, wrq->u.data.pointer, wrq->u.data.length * sizeof(int))) {
PRINTM(MERROR, "Copy from user failed\n");
LEAVE();
return -EFAULT;
}
deep_sleep = data[0];
if (deep_sleep == DEEP_SLEEP_OFF) {
PRINTM(MINFO, "Exit Deep Sleep Mode\n");
woal_set_deep_sleep(priv, MOAL_IOCTL_WAIT, MFALSE, 0);
} else if (deep_sleep == DEEP_SLEEP_ON) {
PRINTM(MINFO, "Enter Deep Sleep Mode\n");
if (wrq->u.data.length == 2)
idletime = data[1];
woal_set_deep_sleep(priv, MOAL_IOCTL_WAIT, MTRUE, idletime);
} else {
PRINTM(MERROR, "Unknown option = %lu\n", deep_sleep);
LEAVE();
return -EINVAL;
}
} else if (wrq->u.data.length > 2) {
PRINTM(MERROR, "Invalid number of arguments %d\n", wrq->u.data.length);
LEAVE();
return -EINVAL;
} else { /* Display Deep Sleep settings */
PRINTM(MINFO, "Get Deep Sleep Mode\n");
if (MLAN_STATUS_SUCCESS != woal_get_deep_sleep(priv, &deep_sleep)) {
LEAVE();
return -EFAULT;
}
}
/* Copy the Deep Sleep setting to user */
if (copy_to_user(wrq->u.data.pointer, &deep_sleep, sizeof(int))) {
PRINTM(MERROR, "Copy to user failed\n");
LEAVE();
return -EINVAL;
}
wrq->u.data.length = 1;
LEAVE();
return 0;
}
/**
* @brief Set/Get Usr 11n configuration request
*
* @param priv Pointer to the moal_private driver data struct
* @param wrq A pointer to iwreq structure
*
* @return 0 --success, otherwise fail
*/
static int
woal_11n_htcap_cfg(moal_private * priv, struct iwreq *wrq)
{
int data;
mlan_ioctl_req *req = NULL;
mlan_ds_11n_cfg *cfg_11n = NULL;
int ret = 0;
ENTER();
if (wrq->u.data.length > 1) {
PRINTM(MERROR, "Invalid number of arguments\n");
ret = -EINVAL;
goto done;
}
if (((req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg))) == NULL)) {
ret = -ENOMEM;
goto done;
}
cfg_11n = (mlan_ds_11n_cfg *) req->pbuf;
cfg_11n->sub_command = MLAN_OID_11N_HTCAP_CFG;
req->req_id = MLAN_IOCTL_11N_CFG;
if (wrq->u.data.length == 0) {
/* Get 11n tx parameters from MLAN */
req->action = MLAN_ACT_GET;
} else if (wrq->u.data.length == 1) {
if (copy_from_user(&data, wrq->u.data.pointer, sizeof(int))) {
PRINTM(MERROR, "Copy from user failed\n");
ret = -EFAULT;
goto done;
}
cfg_11n->param.htcap_cfg = data;
PRINTM(MINFO, "SET: htusrcap:0x%x\n", data);
/* Update 11n tx parameters in MLAN */
req->action = MLAN_ACT_SET;
}
if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
ret = -EFAULT;
goto done;
}
data = cfg_11n->param.htcap_cfg;
PRINTM(MINFO, "GET: httxcap:0x%x\n", data);
if (copy_to_user(wrq->u.data.pointer, &data, sizeof(data))) {
PRINTM(MERROR, "Copy to user failed\n");
ret = -EFAULT;
goto done;
}
wrq->u.data.length = 1;
done:
if (req)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief Enable/Disable amsdu_aggr_ctrl
*
* @param priv Pointer to the moal_private driver data struct
* @param wrq A pointer to iwreq structure
*
* @return 0 --success, otherwise fail
*/
static int
woal_11n_amsdu_aggr_ctrl(moal_private * priv, struct iwreq *wrq)
{
int data[2];
mlan_ioctl_req *req = NULL;
mlan_ds_11n_cfg *cfg_11n = NULL;
int ret = 0;
ENTER();
if ((wrq->u.data.length != 0) && (wrq->u.data.length != 1)) {
PRINTM(MERROR, "Invalid number of arguments\n");
ret = -EINVAL;
goto done;
}
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg));
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
cfg_11n = (mlan_ds_11n_cfg *) req->pbuf;
cfg_11n->sub_command = MLAN_OID_11N_CFG_AMSDU_AGGR_CTRL;
req->req_id = MLAN_IOCTL_11N_CFG;
if (wrq->u.data.length == 0) {
/* Get 11n tx parameters from MLAN */
req->action = MLAN_ACT_GET;
} else if (wrq->u.data.length == 1) {
if (copy_from_user(data, wrq->u.data.pointer,
wrq->u.data.length * sizeof(int))) {
PRINTM(MERROR, "Copy from user failed\n");
ret = -EFAULT;
goto done;
}
cfg_11n->param.amsdu_aggr_ctrl.enable = data[0];
/* Update 11n tx parameters in MLAN */
req->action = MLAN_ACT_SET;
}
if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
ret = -EFAULT;
goto done;
}
data[0] = cfg_11n->param.amsdu_aggr_ctrl.enable;
data[1] = cfg_11n->param.amsdu_aggr_ctrl.curr_buf_size;
if (copy_to_user(wrq->u.data.pointer, data, sizeof(data))) {
PRINTM(MERROR, "Copy to user failed\n");
ret = -EFAULT;
goto done;
}
wrq->u.data.length = 2;
done:
if (req)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief Set/Get 11n configuration request
*
* @param priv Pointer to the moal_private driver data struct
* @param wrq A pointer to iwreq structure
*
* @return 0 --success, otherwise fail
*/
static int
woal_11n_tx_cfg(moal_private * priv, struct iwreq *wrq)
{
int data;
mlan_ioctl_req *req = NULL;
mlan_ds_11n_cfg *cfg_11n = NULL;
int ret = 0;
ENTER();
if ((wrq->u.data.length != 0) && (wrq->u.data.length != 1)) {
PRINTM(MERROR, "Invalid number of arguments\n");
ret = -EINVAL;
goto done;
}
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg));
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
cfg_11n = (mlan_ds_11n_cfg *) req->pbuf;
cfg_11n->sub_command = MLAN_OID_11N_CFG_TX;
req->req_id = MLAN_IOCTL_11N_CFG;
if (wrq->u.data.length == 0) {
/* Get 11n tx parameters from MLAN */
req->action = MLAN_ACT_GET;
} else if (wrq->u.data.length == 1) {
if (copy_from_user(&data, wrq->u.data.pointer, sizeof(data))) {
PRINTM(MERROR, "Copy from user failed\n");
ret = -EFAULT;
goto done;
}
cfg_11n->param.tx_cfg.httxcap = data;
PRINTM(MINFO, "SET: httxcap:%d\n", data);
/* Update 11n tx parameters in MLAN */
req->action = MLAN_ACT_SET;
}
if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
ret = -EFAULT;
goto done;
}
data = cfg_11n->param.tx_cfg.httxcap;
PRINTM(MINFO, "GET: httxcap:%d\n", data);
if (copy_to_user(wrq->u.data.pointer, &data, sizeof(data))) {
PRINTM(MERROR, "Copy to user failed\n");
ret = -EFAULT;
goto done;
}
wrq->u.data.length = 1;
done:
if (req)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief Enable/Disable TX Aggregation
*
* @param priv Pointer to the moal_private driver data struct
* @param wrq A pointer to iwreq structure
*
* @return 0 --success, otherwise fail
*/
static int
woal_11n_prio_tbl(moal_private * priv, struct iwreq *wrq)
{
int data[MAX_NUM_TID * 2], i, j;
mlan_ioctl_req *req = NULL;
mlan_ds_11n_cfg *cfg_11n = NULL;
int ret = 0;
ENTER();
if ((wrq->u.data.pointer == NULL)) {
LEAVE();
return -EINVAL;
}
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg));
if (req == NULL) {
LEAVE();
return -ENOMEM;
}
cfg_11n = (mlan_ds_11n_cfg *) req->pbuf;
cfg_11n->sub_command = MLAN_OID_11N_CFG_AGGR_PRIO_TBL;
req->req_id = MLAN_IOCTL_11N_CFG;
if (wrq->u.data.length == 0) {
/* Get aggr priority table from MLAN */
req->action = MLAN_ACT_GET;
if (MLAN_STATUS_SUCCESS !=
woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
ret = -EFAULT;
goto error;
}
wrq->u.data.length = MAX_NUM_TID * 2;
for (i = 0, j = 0; i < (wrq->u.data.length); i = i + 2, ++j) {
data[i] = cfg_11n->param.aggr_prio_tbl.ampdu[j];
data[i + 1] = cfg_11n->param.aggr_prio_tbl.amsdu[j];
}
if (copy_to_user(wrq->u.data.pointer, data,
sizeof(int) * wrq->u.data.length)) {
PRINTM(MERROR, "Copy to user failed\n");
ret = -EFAULT;
goto error;
}
} else if (wrq->u.data.length == 16) {
if (copy_from_user(data, wrq->u.data.pointer,
sizeof(int) * wrq->u.data.length)) {
PRINTM(MERROR, "Copy from user failed\n");
ret = -EFAULT;
goto error;
}
for (i = 0, j = 0; i < (wrq->u.data.length); i = i + 2, ++j) {
cfg_11n->param.aggr_prio_tbl.ampdu[j] = data[i];
cfg_11n->param.aggr_prio_tbl.amsdu[j] = data[i + 1];
}
/* Update aggr priority table in MLAN */
req->action = MLAN_ACT_SET;
if (MLAN_STATUS_SUCCESS !=
woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
ret = -EFAULT;
goto error;
}
} else {
PRINTM(MERROR, "Invalid number of arguments\n");
ret = -EINVAL;
goto error;
}
error:
if (req)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief Set/Get add BA Reject paramters
*
* @param priv Pointer to the moal_private driver data struct
* @param wrq A pointer to iwreq structure
*
* @return 0 --success, otherwise fail
*/
static int
woal_addba_reject(moal_private * priv, struct iwreq *wrq)
{
int data[MAX_NUM_TID], ret = 0, i;
mlan_ioctl_req *req = NULL;
mlan_ds_11n_cfg *cfg_11n = NULL;
ENTER();
PRINTM(MERROR, "%s\n", __FUNCTION__);
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg));
if (req == NULL) {
LEAVE();
return -ENOMEM;
}
cfg_11n = (mlan_ds_11n_cfg *) req->pbuf;
cfg_11n->sub_command = MLAN_OID_11N_CFG_ADDBA_REJECT;
req->req_id = MLAN_IOCTL_11N_CFG;
if (wrq->u.data.length == 0) {
PRINTM(MERROR, "Addba reject moal\n");
/* Get aggr priority table from MLAN */
req->action = MLAN_ACT_GET;
if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req,
MOAL_IOCTL_WAIT)) {
ret = -EFAULT;
goto error;
}
wrq->u.data.length = MAX_NUM_TID;
for (i = 0; i < (wrq->u.data.length); ++i) {
data[i] = cfg_11n->param.addba_reject[i];
}
if (copy_to_user(wrq->u.data.pointer, data,
sizeof(int) * wrq->u.data.length)) {
PRINTM(MERROR, "Copy to user failed\n");
ret = -EFAULT;
goto error;
}
} else if (wrq->u.data.length == 8) {
if (copy_from_user(data, wrq->u.data.pointer,
sizeof(int) * wrq->u.data.length)) {
PRINTM(MERROR, "Copy from user failed\n");
ret = -EFAULT;
goto error;
}
for (i = 0; i < (wrq->u.data.length); ++i) {
cfg_11n->param.addba_reject[i] = data[i];
}
/* Update aggr priority table in MLAN */
req->action = MLAN_ACT_SET;
if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req,
MOAL_IOCTL_WAIT)) {
ret = -EFAULT;
goto error;
}
} else {
PRINTM(MERROR, "Invalid number of arguments\n");
ret = -EINVAL;
goto error;
}
error:
if (req)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief Set/Get add BA paramters
*
* @param priv Pointer to the moal_private driver data struct
* @param wrq A pointer to iwreq structure
*
* @return 0 --success, otherwise fail
*/
static int
woal_addba_para_updt(moal_private * priv, struct iwreq *wrq)
{
int data[3], ret = 0;
mlan_ioctl_req *req = NULL;
mlan_ds_11n_cfg *cfg_11n = NULL;
ENTER();
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg));
if (req == NULL) {
LEAVE();
return -ENOMEM;
}
cfg_11n = (mlan_ds_11n_cfg *) req->pbuf;
cfg_11n->sub_command = MLAN_OID_11N_CFG_ADDBA_PARAM;
req->req_id = MLAN_IOCTL_11N_CFG;
if (wrq->u.data.length == 0) {
/* Get Add BA parameters from MLAN */
req->action = MLAN_ACT_GET;
if (MLAN_STATUS_SUCCESS !=
woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
ret = -EFAULT;
goto error;
}
data[0] = cfg_11n->param.addba_param.timeout;
data[1] = cfg_11n->param.addba_param.txwinsize;
data[2] = cfg_11n->param.addba_param.rxwinsize;
PRINTM(MINFO, "GET: timeout:%d txwinsize:%d rxwinsize:%d\n", data[0],
data[1], data[2]);
wrq->u.data.length = 3;
if (copy_to_user(wrq->u.data.pointer, data,
wrq->u.data.length * sizeof(int))) {
PRINTM(MERROR, "Copy to user failed\n");
ret = -EFAULT;
goto error;
}
} else if (wrq->u.data.length == 3) {
if (copy_from_user
(data, wrq->u.data.pointer, wrq->u.data.length * sizeof(int))) {
PRINTM(MERROR, "Copy from user failed\n");
ret = -EFAULT;
goto error;
}
cfg_11n->param.addba_param.timeout = data[0];
cfg_11n->param.addba_param.txwinsize = data[1];
cfg_11n->param.addba_param.rxwinsize = data[2];
PRINTM(MINFO, "SET: timeout:%d txwinsize:%d rxwinsize:%d\n", data[0],
data[1], data[2]);
/* Update Add BA parameters in MLAN */
req->action = MLAN_ACT_SET;
if (MLAN_STATUS_SUCCESS !=
woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
ret = MLAN_STATUS_FAILURE;
goto error;
}
} else {
PRINTM(MERROR, "Invalid number of arguments\n");
ret = -EINVAL;
goto error;
}
error:
if (req)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief Set/Get Transmit buffer size
*
* @param priv Pointer to the moal_private driver data struct
* @param wrq A pointer to iwreq structure
*
* @return 0 --success, otherwise fail
*/
static int
woal_txbuf_cfg(moal_private * priv, struct iwreq *wrq)
{
int buf_size;
mlan_ioctl_req *req = NULL;
mlan_ds_11n_cfg *cfg_11n = NULL;
int ret = 0;
ENTER();
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg));
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
cfg_11n = (mlan_ds_11n_cfg *) req->pbuf;
cfg_11n->sub_command = MLAN_OID_11N_CFG_MAX_TX_BUF_SIZE;
req->req_id = MLAN_IOCTL_11N_CFG;
if (wrq->u.data.length == 0) {
/* Get Tx buffer size from MLAN */
req->action = MLAN_ACT_GET;
} else {
if (copy_from_user(&buf_size, wrq->u.data.pointer, sizeof(buf_size))) {
PRINTM(MERROR, "Copy from user failed\n");
ret = -EFAULT;
goto done;
}
PRINTM(MINFO, "SET: Tx buffer size %d\n", buf_size);
/* Update Tx buffer size in MLAN */
req->action = MLAN_ACT_SET;
cfg_11n->param.tx_buf_size = buf_size;
}
if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
ret = -EFAULT;
goto done;
}
buf_size = cfg_11n->param.tx_buf_size;
if (copy_to_user(wrq->u.data.pointer, &buf_size, sizeof(buf_size))) {
PRINTM(MERROR, "Copy to user failed\n");
ret = -EFAULT;
goto done;
}
wrq->u.data.length = 1;
done:
if (req)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief Set/Get Host Sleep configuration
*
* @param priv A pointer to moal_private structure
* @param wrq A pointer to iwreq structure
* @param invoke_hostcmd MTRUE --invoke HostCmd, otherwise MFALSE
*
* @return 0 --success, otherwise fail
*/
static int
woal_hs_cfg(moal_private * priv, struct iwreq *wrq, BOOLEAN invoke_hostcmd)
{
int data[3];
int ret = 0;
mlan_ioctl_req *req = NULL;
mlan_ds_pm_cfg *pmcfg = NULL;
mlan_ds_hs_cfg hscfg;
int data_length = wrq->u.data.length;
ENTER();
memset(data, 0, sizeof(data));
memset(&hscfg, 0, sizeof(mlan_ds_hs_cfg));
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_pm_cfg));
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
pmcfg = (mlan_ds_pm_cfg *) req->pbuf;
pmcfg->sub_command = MLAN_OID_PM_CFG_HS_CFG;
req->req_id = MLAN_IOCTL_PM_CFG;
if (data_length == 0) {
req->action = MLAN_ACT_GET;
} else {
req->action = MLAN_ACT_SET;
if (data_length >= 1 && data_length <= 3) {
if (copy_from_user
(data, wrq->u.data.pointer, sizeof(int) * data_length)) {
PRINTM(MERROR, "Copy from user failed\n");
ret = -EFAULT;
goto done;
}
} else {
PRINTM(MERROR, "Invalid arguments\n");
ret = -EINVAL;
goto done;
}
}
/* Do a GET first if all arguments are not available */
if (data_length >= 1 && data_length < 3) {
woal_get_hs_params(priv, MOAL_IOCTL_WAIT, &hscfg);
}
switch (data_length) {
case 3: /* Conditions, GPIO, GAP provided */
pmcfg->param.hs_cfg.conditions = data[0];
pmcfg->param.hs_cfg.gpio = data[1];
pmcfg->param.hs_cfg.gap = data[2];
break;
case 2: /* Conditions, GPIO provided */
pmcfg->param.hs_cfg.conditions = data[0];
pmcfg->param.hs_cfg.gpio = data[1];
pmcfg->param.hs_cfg.gap = hscfg.gap;
break;
case 1: /* Conditions provided */
pmcfg->param.hs_cfg.conditions = data[0];
pmcfg->param.hs_cfg.gpio = hscfg.gpio;
pmcfg->param.hs_cfg.gap = hscfg.gap;
break;
case 0:
break;
default:
break;
}
pmcfg->param.hs_cfg.is_invoke_hostcmd = invoke_hostcmd;
if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
ret = -EFAULT;
goto done;
}
if (req->action == MLAN_ACT_GET) {
data[0] = pmcfg->param.hs_cfg.conditions;
data[1] = pmcfg->param.hs_cfg.gpio;
data[2] = pmcfg->param.hs_cfg.gap;
wrq->u.data.length = 3;
if (copy_to_user
(wrq->u.data.pointer, data, sizeof(int) * wrq->u.data.length)) {
PRINTM(MERROR, "Copy to user failed\n");
ret = -EFAULT;
goto done;
}
}
done:
if (req)
kfree(req);
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_hs_setpara(moal_private * priv, struct iwreq *wrq)
{
int ret = 0;
int data_length = wrq->u.data.length;
ENTER();
if (data_length >= 1 && data_length <= 3) {
ret = woal_hs_cfg(priv, wrq, MFALSE);
goto done;
} else {
PRINTM(MERROR, "Invalid arguments\n");
ret = -EINVAL;
goto done;
}
done:
LEAVE();
return ret;
}
/**
* @brief Get/Set inactivity timeout extend
*
* @param priv A pointer to moal_private structure
* @param wrq A pointer to iwreq structure
*
* @return 0 --success, otherwise fail
*/
static int
woal_inactivity_timeout_ext(moal_private * priv, struct iwreq *wrq)
{
int data[4];
int ret = 0;
mlan_ioctl_req *req = NULL;
mlan_ds_pm_cfg *pmcfg = NULL;
pmlan_ds_inactivity_to inac_to = NULL;
int data_length = wrq->u.data.length;
ENTER();
memset(data, 0, sizeof(data));
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_pm_cfg));
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
pmcfg = (mlan_ds_pm_cfg *) req->pbuf;
inac_to = &pmcfg->param.inactivity_to;
pmcfg->sub_command = MLAN_OID_PM_CFG_INACTIVITY_TO;
req->req_id = MLAN_IOCTL_PM_CFG;
if ((data_length != 0) && (data_length != 3) && (data_length != 4)) {
ret = -EINVAL;
goto done;
}
req->action = MLAN_ACT_GET;
if (data_length) {
if (copy_from_user
(data, wrq->u.data.pointer, sizeof(int) * data_length)) {
PRINTM(MERROR, "Copy from user failed\n");
ret = -EFAULT;
goto done;
}
req->action = MLAN_ACT_SET;
inac_to->timeout_unit = data[0];
inac_to->unicast_timeout = data[1];
inac_to->mcast_timeout = data[2];
inac_to->ps_entry_timeout = data[3];
}
if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
ret = -EFAULT;
goto done;
}
/* Copy back current values regardless of GET/SET */
data[0] = inac_to->timeout_unit;
data[1] = inac_to->unicast_timeout;
data[2] = inac_to->mcast_timeout;
data[3] = inac_to->ps_entry_timeout;
if (req->action == MLAN_ACT_GET) {
wrq->u.data.length = 4;
if (copy_to_user
(wrq->u.data.pointer, data, wrq->u.data.length * sizeof(int))) {
PRINTM(MERROR, "Copy to user failed\n");
ret = -EFAULT;
goto done;
}
}
done:
if (req)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief Set/Get system clock
*
* @param priv A pointer to moal_private structure
* @param wrq A pointer to iwreq structure
*
* @return 0 --success, otherwise fail
*/
static int
woal_ecl_sys_clock(moal_private * priv, struct iwreq *wrq)
{
int data[64];
int ret = 0;
mlan_ioctl_req *req = NULL;
mlan_ds_misc_cfg *cfg = NULL;
int data_length = wrq->u.data.length;
int i = 0;
ENTER();
memset(data, 0, sizeof(data));
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg));
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
cfg = (mlan_ds_misc_cfg *) req->pbuf;
cfg->sub_command = MLAN_OID_MISC_SYS_CLOCK;
req->req_id = MLAN_IOCTL_MISC_CFG;
if (!data_length)
req->action = MLAN_ACT_GET;
else if (data_length <= MLAN_MAX_CLK_NUM) {
req->action = MLAN_ACT_SET;
if (copy_from_user
(data, wrq->u.data.pointer, sizeof(int) * data_length)) {
PRINTM(MERROR, "Copy from user failed\n");
ret = -EFAULT;
goto done;
}
} else {
PRINTM(MERROR, "Invalid arguments\n");
ret = -EINVAL;
goto done;
}
if (req->action == MLAN_ACT_GET) {
/* Get configurable clocks */
cfg->param.sys_clock.sys_clk_type = MLAN_CLK_CONFIGURABLE;
if (MLAN_STATUS_SUCCESS !=
woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
ret = -EFAULT;
goto done;
}
/* Current system clock */
data[0] = (int) cfg->param.sys_clock.cur_sys_clk;
wrq->u.data.length = 1;
data_length = MIN(cfg->param.sys_clock.sys_clk_num, MLAN_MAX_CLK_NUM);
/* Configurable clocks */
for (i = 0; i < data_length; i++) {
data[i + wrq->u.data.length] =
(int) cfg->param.sys_clock.sys_clk[i];
}
wrq->u.data.length += data_length;
/* Get supported clocks */
cfg->param.sys_clock.sys_clk_type = MLAN_CLK_SUPPORTED;
if (MLAN_STATUS_SUCCESS !=
woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
ret = -EFAULT;
goto done;
}
data_length = MIN(cfg->param.sys_clock.sys_clk_num, MLAN_MAX_CLK_NUM);
/* Supported clocks */
for (i = 0; i < data_length; i++) {
data[i + wrq->u.data.length] =
(int) cfg->param.sys_clock.sys_clk[i];
}
wrq->u.data.length += data_length;
if (copy_to_user
(wrq->u.data.pointer, data, sizeof(int) * wrq->u.data.length)) {
PRINTM(MERROR, "Copy to user failed\n");
ret = -EFAULT;
goto done;
}
} else {
/* Set configurable clocks */
cfg->param.sys_clock.sys_clk_type = MLAN_CLK_CONFIGURABLE;
cfg->param.sys_clock.sys_clk_num = MIN(MLAN_MAX_CLK_NUM, data_length);
for (i = 0; i < cfg->param.sys_clock.sys_clk_num; i++) {
cfg->param.sys_clock.sys_clk[i] = (t_u16) data[i];
}
if (MLAN_STATUS_SUCCESS !=
woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
ret = -EFAULT;
goto done;
}
}
done:
if (req)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief Set/Get Band and Adhoc-band setting
*
* @param priv A pointer to moal_private structure
* @param wrq A pointer to iwreq structure
*
* @return 0 --success, otherwise fail
*/
static int
woal_band_cfg(moal_private * priv, struct iwreq *wrq)
{
int ret = 0, i;
int data[3];
int user_data_len = wrq->u.data.length;
t_u32 infra_band = 0;
t_u32 adhoc_band = 0;
t_u32 adhoc_channel = 0;
mlan_ioctl_req *req = NULL;
mlan_ds_radio_cfg *radio_cfg = NULL;
ENTER();
if (user_data_len > 3) {
LEAVE();
return -EINVAL;
}
if (user_data_len > 0) {
if (priv->media_connected == MTRUE) {
LEAVE();
return -EOPNOTSUPP;
}
}
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_radio_cfg));
if (req == NULL) {
ret = -ENOMEM;
goto error;
}
radio_cfg = (mlan_ds_radio_cfg *) req->pbuf;
radio_cfg->sub_command = MLAN_OID_BAND_CFG;
req->req_id = MLAN_IOCTL_RADIO_CFG;
if (wrq->u.data.length == 0) {
/* Get config_bands, adhoc_start_band and adhoc_channnel values from
MLAN */
req->action = MLAN_ACT_GET;
if (MLAN_STATUS_SUCCESS !=
woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
ret = -EFAULT;
goto error;
}
data[0] = radio_cfg->param.band_cfg.config_bands; /* Infra Band */
data[1] = radio_cfg->param.band_cfg.adhoc_start_band; /* Adhoc Band */
data[2] = radio_cfg->param.band_cfg.adhoc_channel; /* Adhoc
Channel */
wrq->u.data.length = 3;
if (copy_to_user
(wrq->u.data.pointer, data, sizeof(int) * wrq->u.data.length)) {
ret = -EFAULT;
goto error;
}
} else {
if (copy_from_user
(data, wrq->u.data.pointer, sizeof(int) * wrq->u.data.length)) {
PRINTM(MERROR, "Copy from user failed\n");
ret = -EFAULT;
goto error;
}
/* To support only <b/bg/bgn/n> */
infra_band = data[0];
for (i = 0; i < sizeof(SupportedInfraBand); i++)
if (infra_band == SupportedInfraBand[i])
break;
if (i == sizeof(SupportedInfraBand)) {
ret = -EINVAL;
goto error;
}
/* Set Adhoc band */
if (user_data_len >= 2) {
adhoc_band = data[1];
for (i = 0; i < sizeof(SupportedAdhocBand); i++)
if (adhoc_band == SupportedAdhocBand[i])
break;
if (i == sizeof(SupportedAdhocBand)) {
ret = -EINVAL;
goto error;
}
}
/* Set Adhoc channel */
if (user_data_len == 3) {
adhoc_channel = data[2];
if (adhoc_channel == 0) {
/* Check if specified adhoc channel is non-zero */
ret = -EINVAL;
goto error;
}
}
/* Set config_bands and adhoc_start_band values to MLAN */
req->action = MLAN_ACT_SET;
radio_cfg->param.band_cfg.config_bands = infra_band;
radio_cfg->param.band_cfg.adhoc_start_band = adhoc_band;
radio_cfg->param.band_cfg.adhoc_channel = adhoc_channel;
if (MLAN_STATUS_SUCCESS !=
woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
ret = -EFAULT;
goto error;
}
}
error:
if (req)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief Set/Get BCA timeshare
*
* @param priv A pointer to moal_private structure
* @param wrq A pointer to iwreq structure
*
* @return 0 --success, otherwise fail
*/
static int
woal_bca_time_share(moal_private * priv, struct iwreq *wrq)
{
int ret = 0;
int data[3];
int user_data_len = wrq->u.data.length;
mlan_ioctl_req *req = NULL;
mlan_ds_bca_cfg *bca_cfg = NULL;
ENTER();
if (user_data_len != 1 && user_data_len != 3) {
PRINTM(MERROR, "Invalid number of parameters\n");
LEAVE();
return -EINVAL;
}
if (copy_from_user(data, wrq->u.data.pointer, sizeof(int) * user_data_len)) {
PRINTM(MERROR, "Copy from user failed\n");
LEAVE();
return -EFAULT;
}
if ((data[0] > TRAFFIC_TYPE_MEDIUMHIGH) && (data[0] != TRAFFIC_TYPE_RESET)) {
PRINTM(MERROR, "Invalid traffic_type\n");
LEAVE();
return -EINVAL;
}
if (user_data_len == 3) {
/* Valid Range for timeshare_interval: < 20 ... 60_000 > ms */
if (data[1] < MIN_TIMESHARE_INTERVAL ||
data[1] > MAX_TIMESHARE_INTERVAL) {
PRINTM(MERROR,
"Invalid timeshare_interval Range: < 20 ... 60000 > ms\n");
LEAVE();
return -EINVAL;
}
data[1] -= (data[1] % 10); /* If value is not multiple of 10 then
take the floor value */
/* If value is not multiple of 10 then take the floor value */
data[2] -= (data[2] % 10);
/* Valid Range for timeshare_interval: < 0 ... bt_time > */
if (data[2] < 0 || data[2] > data[1]) {
PRINTM(MERROR,
"Invalid bt_time Range: < 0 ... timeshare_interval > ms\n");
LEAVE();
return -EINVAL;
}
}
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bca_cfg));
if (req == NULL) {
ret = -ENOMEM;
goto error;
}
bca_cfg = (mlan_ds_bca_cfg *) req->pbuf;
bca_cfg->sub_command = MLAN_OID_BCA_TS;
req->req_id = MLAN_IOCTL_BCA_CFG;
req->action = MLAN_ACT_GET;
if (user_data_len > 0)
bca_cfg->param.bca_ts.traffic_type = data[0];
if (user_data_len == 3) {
req->action = MLAN_ACT_SET;
bca_cfg->param.bca_ts.timeshare_interval = data[1];
bca_cfg->param.bca_ts.bt_time = data[2];
}
PRINTM(MINFO, "traffic_type=0x%x", data[0]);
if (user_data_len == 3)
PRINTM(MINFO, " timeshare_interval=0x%x bt_time=0x%x\n", data[1],
data[2]);
/* Set/Get BCA time share values from MLAN */
if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
ret = -EFAULT;
goto error;
}
if (user_data_len < 3) {
data[0] = bca_cfg->param.bca_ts.traffic_type;
data[1] = bca_cfg->param.bca_ts.timeshare_interval;
data[2] = bca_cfg->param.bca_ts.bt_time;
wrq->u.data.length = 3;
if (copy_to_user(wrq->u.data.pointer, data, sizeof(data))) {
PRINTM(MERROR, "Copy to user failed\n");
ret = -EFAULT;
goto error;
}
}
error:
if (req)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief Read/Write adapter registers value
*
* @param priv A pointer to moal_private structure
* @param wrq A pointer to iwreq structure
*
* @return 0 --success, otherwise fail
*/
static int
woal_reg_read_write(moal_private * priv, struct iwreq *wrq)
{
int data[3];
int ret = 0;
mlan_ioctl_req *req = NULL;
mlan_ds_reg_mem *reg = NULL;
int data_length = wrq->u.data.length;
ENTER();
memset(data, 0, sizeof(data));
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_reg_mem));
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
reg = (mlan_ds_reg_mem *) req->pbuf;
reg->sub_command = MLAN_OID_REG_RW;
req->req_id = MLAN_IOCTL_REG_MEM;
if (data_length == 2) {
req->action = MLAN_ACT_GET;
} else if (data_length == 3) {
req->action = MLAN_ACT_SET;
} else {
ret = -EINVAL;
goto done;
}
if (copy_from_user(data, wrq->u.data.pointer, sizeof(int) * data_length)) {
PRINTM(MERROR, "Copy from user failed\n");
ret = -EFAULT;
goto done;
}
reg->param.reg_rw.type = (t_u32) data[0];
reg->param.reg_rw.offset = (t_u32) data[1];
if (data_length == 3)
reg->param.reg_rw.value = (t_u32) data[2];
if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
ret = -EFAULT;
goto done;
}
if (req->action == MLAN_ACT_GET) {
if (copy_to_user
(wrq->u.data.pointer, &reg->param.reg_rw.value, sizeof(int))) {
PRINTM(MERROR, "Copy to user failed\n");
ret = -EFAULT;
goto done;
}
wrq->u.data.length = 1;
}
done:
if (req)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief Read the EEPROM contents of the card
*
* @param priv A pointer to moal_private structure
* @param wrq A pointer to iwreq structure
*
* @return 0 --success, otherwise fail
*/
static int
woal_read_eeprom(moal_private * priv, struct iwreq *wrq)
{
int data[2];
int ret = 0;
mlan_ioctl_req *req = NULL;
mlan_ds_reg_mem *reg = NULL;
int data_length = wrq->u.data.length;
ENTER();
memset(data, 0, sizeof(data));
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_reg_mem));
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
reg = (mlan_ds_reg_mem *) req->pbuf;
reg->sub_command = MLAN_OID_EEPROM_RD;
req->req_id = MLAN_IOCTL_REG_MEM;
if (data_length == 2) {
req->action = MLAN_ACT_GET;
} else {
ret = -EINVAL;
goto done;
}
if (copy_from_user(data, wrq->u.data.pointer, sizeof(int) * data_length)) {
PRINTM(MERROR, "Copy from user failed\n");
ret = -EFAULT;
goto done;
}
reg->param.rd_eeprom.offset = (t_u16) data[0];
reg->param.rd_eeprom.byte_count = (t_u16) data[1];
if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
ret = -EFAULT;
goto done;
}
if (req->action == MLAN_ACT_GET) {
wrq->u.data.length = reg->param.rd_eeprom.byte_count;
if (copy_to_user
(wrq->u.data.pointer, reg->param.rd_eeprom.value,
wrq->u.data.length)) {
PRINTM(MERROR, "Copy to user failed\n");
ret = -EFAULT;
goto done;
}
}
done:
if (req)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief Read/Write device memory value
*
* @param priv A pointer to moal_private structure
* @param wrq A pointer to iwreq structure
*
* @return 0 --success, otherwise fail
*/
static int
woal_mem_read_write(moal_private * priv, struct iwreq *wrq)
{
t_u32 data[2];
int ret = 0;
mlan_ioctl_req *req = NULL;
mlan_ds_reg_mem *reg_mem = NULL;
int data_length = wrq->u.data.length;
ENTER();
memset(data, 0, sizeof(data));
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_reg_mem));
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
reg_mem = (mlan_ds_reg_mem *) req->pbuf;
reg_mem->sub_command = MLAN_OID_MEM_RW;
req->req_id = MLAN_IOCTL_REG_MEM;
if (data_length == 1) {
PRINTM(MINFO, "MEM_RW: GET\n");
req->action = MLAN_ACT_GET;
} else if (data_length == 2) {
PRINTM(MINFO, "MEM_RW: SET\n");
req->action = MLAN_ACT_SET;
} else {
ret = -EINVAL;
goto done;
}
if (copy_from_user(data, wrq->u.data.pointer, sizeof(int) * data_length)) {
PRINTM(MERROR, "Copy from user failed\n");
ret = -EFAULT;
goto done;
}
reg_mem->param.mem_rw.addr = (t_u32) data[0];
if (data_length == 2)
reg_mem->param.mem_rw.value = (t_u32) data[1];
PRINTM(MINFO, "MEM_RW: Addr=0x%x, Value=0x%x\n",
(int) reg_mem->param.mem_rw.addr, (int) reg_mem->param.mem_rw.value);
if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
ret = -EFAULT;
goto done;
}
if (req->action == MLAN_ACT_GET) {
if (copy_to_user
(wrq->u.data.pointer, &reg_mem->param.mem_rw.value, sizeof(int))) {
PRINTM(MERROR, "Copy to user failed\n");
ret = -EFAULT;
goto done;
}
wrq->u.data.length = 1;
}
done:
if (req)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief Get LOG
*
* @param priv A pointer to moal_private structure
* @param wrq A pointer to iwreq structure
*
* @return 0 --success, otherwise fail
*/
static int
woal_get_log(moal_private * priv, struct iwreq *wrq)
{
int ret = 0;
mlan_ds_get_stats stats;
char *buf = NULL;
ENTER();
PRINTM(MINFO, " GET STATS\n");
if (!(buf = kmalloc(GETLOG_BUFSIZE, GFP_ATOMIC))) {
PRINTM(MERROR, "kmalloc failed!\n");
ret = -ENOMEM;
goto done;
}
memset(&stats, 0, sizeof(mlan_ds_get_stats));
if (MLAN_STATUS_SUCCESS !=
woal_get_stats_info(priv, MOAL_IOCTL_WAIT, &stats)) {
ret = -EFAULT;
goto done;
}
if (wrq->u.data.pointer) {
sprintf(buf, "\n"
"mcasttxframe %lu\n"
"failed %lu\n"
"retry %lu\n"
"multiretry %lu\n"
"framedup %lu\n"
"rtssuccess %lu\n"
"rtsfailure %lu\n"
"ackfailure %lu\n"
"rxfrag %lu\n"
"mcastrxframe %lu\n"
"fcserror %lu\n"
"txframe %lu\n"
"wepicverrcnt-1 %lu\n"
"wepicverrcnt-2 %lu\n"
"wepicverrcnt-3 %lu\n"
"wepicverrcnt-4 %lu\n",
stats.mcast_tx_frame,
stats.failed,
stats.retry,
stats.multi_retry,
stats.frame_dup,
stats.rts_success,
stats.rts_failure,
stats.ack_failure,
stats.rx_frag,
stats.mcast_rx_frame,
stats.fcs_error,
stats.tx_frame,
stats.wep_icv_error[0],
stats.wep_icv_error[1],
stats.wep_icv_error[2], stats.wep_icv_error[3]);
wrq->u.data.length = strlen(buf) + 1;
if (copy_to_user(wrq->u.data.pointer, buf, wrq->u.data.length)) {
PRINTM(MERROR, "Copy to user failed\n");
ret = -EFAULT;
}
}
done:
if (buf)
kfree(buf);
LEAVE();
return ret;
}
/**
* @brief Deauthenticate
*
* @param priv A pointer to moal_private structure
* @param wrq A pointer to iwreq structure
*
* @return 0 --success, otherwise fail
*/
static int
woal_deauth(moal_private * priv, struct iwreq *wrq)
{
int ret = 0;
struct sockaddr saddr;
ENTER();
if (wrq->u.data.length) {
/* Deauth mentioned BSSID */
if (copy_from_user(&saddr, wrq->u.data.pointer, sizeof(saddr))) {
PRINTM(MERROR, "Copy from user failed\n");
ret = -EFAULT;
goto done;
}
if (MLAN_STATUS_SUCCESS !=
woal_disconnect(priv, MOAL_IOCTL_WAIT, (t_u8 *) saddr.sa_data))
ret = -EFAULT;
} else {
if (MLAN_STATUS_SUCCESS != woal_disconnect(priv, MOAL_IOCTL_WAIT, NULL))
ret = -EFAULT;
}
done:
LEAVE();
return ret;
}
/**
* @brief Set/Get TX power configurations
*
* @param priv A pointer to moal_private structure
* @param wrq A pointer to iwreq structure
*
* @return 0 --success, otherwise fail
*/
static int
woal_tx_power_cfg(moal_private * priv, struct iwreq *wrq)
{
int data[5], user_data_len;
int ret = 0;
mlan_bss_info bss_info;
mlan_ds_power_cfg *pcfg = NULL;
mlan_ioctl_req *req = NULL;
ENTER();
memset(&bss_info, 0, sizeof(bss_info));
woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info);
memset(data, 0, sizeof(data));
user_data_len = wrq->u.data.length;
if (user_data_len) {
if (copy_from_user
(data, wrq->u.data.pointer, sizeof(int) * user_data_len)) {
PRINTM(MERROR, "Copy from user failed\n");
ret = -EFAULT;
goto done;
}
switch (user_data_len) {
case 1:
if (data[0] != 0xFF)
ret = -EINVAL;
break;
case 2:
case 4:
if (data[0] == 0xFF) {
ret = -EINVAL;
break;
}
if (data[1] < bss_info.min_power_level) {
PRINTM(MERROR,
"The set powercfg rate value %d dBm is out of range (%d dBm-%d dBm)!\n",
data[1], (int) bss_info.min_power_level,
(int) bss_info.max_power_level);
ret = -EINVAL;
break;
}
if (user_data_len == 4) {
if (data[1] > data[2]) {
PRINTM(MERROR, "Min power should be less than maximum!\n");
ret = -EINVAL;
break;
}
if (data[3] < 0) {
PRINTM(MERROR, "Step should not less than 0!\n");
ret = -EINVAL;
break;
}
if (data[2] > bss_info.max_power_level) {
PRINTM(MERROR,
"The set powercfg rate value %d dBm is out of range (%d dBm-%d dBm)!\n",
data[2], (int) bss_info.min_power_level,
(int) bss_info.max_power_level);
ret = -EINVAL;
break;
}
if (data[3] > data[2] - data[1]) {
PRINTM(MERROR,
"Step should not greater than power difference!\n");
ret = -EINVAL;
break;
}
}
break;
default:
ret = -EINVAL;
break;
}
if (ret)
goto done;
}
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_power_cfg));
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
pcfg = (mlan_ds_power_cfg *) req->pbuf;
pcfg->sub_command = MLAN_OID_POWER_CFG_EXT;
req->req_id = MLAN_IOCTL_POWER_CFG;
if (!user_data_len)
req->action = MLAN_ACT_GET;
else {
req->action = MLAN_ACT_SET;
pcfg->param.power_ext.len = user_data_len;
memcpy((t_u8 *) & pcfg->param.power_ext.power_data, (t_u8 *) data,
sizeof(data));
}
if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
ret = -EFAULT;
goto done;
}
if (!user_data_len) {
if (copy_to_user
(wrq->u.data.pointer, (t_u8 *) & pcfg->param.power_ext.power_data,
sizeof(int) * pcfg->param.power_ext.len)) {
ret = -EFAULT;
goto done;
}
wrq->u.data.length = pcfg->param.power_ext.len;
}
done:
if (req)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief Get Tx/Rx data rates
*
* @param priv A pointer to moal_private structure
* @param wrq A pointer to iwreq structure
*
* @return 0 --success, otherwise fail
*/
static int
woal_get_txrx_rate(moal_private * priv, struct iwreq *wrq)
{
int ret = 0;
mlan_ds_rate *rate = NULL;
mlan_ioctl_req *req = NULL;
ENTER();
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_rate));
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
rate = (mlan_ds_rate *) req->pbuf;
rate->sub_command = MLAN_OID_GET_DATA_RATE;
req->req_id = MLAN_IOCTL_RATE;
req->action = MLAN_ACT_GET;
if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
ret = -EFAULT;
goto done;
}
if (copy_to_user
(wrq->u.data.pointer, (t_u8 *) & rate->param.data_rate,
sizeof(int) * 2)) {
ret = -EFAULT;
goto done;
}
wrq->u.data.length = 2;
done:
if (req)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief Turn on/off the sdio clock
*
* @param priv A pointer to moal_private structure
* @param wrq A pointer to iwreq structure
*
* @return 0 --success, otherwise fail
*/
static int
woal_sdio_clock_ioctl(moal_private * priv, struct iwreq *wrq)
{
int ret = 0;
int data = 2;
/* Initialize the clock state as on */
static int clock_state = 1;
ENTER();
if (wrq->u.data.length) {
if (copy_from_user(&data, wrq->u.data.pointer, sizeof(int))) {
PRINTM(MERROR, "Copy from user failed\n");
ret = -EFAULT;
goto done;
}
} else {
wrq->u.data.length = sizeof(clock_state) / sizeof(int);
if (copy_to_user(wrq->u.data.pointer, &clock_state, sizeof(int))) {
PRINTM(MERROR, "Copy from user failed\n");
ret = -EFAULT;
goto done;
}
goto done;
}
switch (data) {
case CMD_DISABLED:
PRINTM(MINFO, "SDIO clock is turned off\n");
ret = woal_sdio_set_bus_clock(priv->phandle, MFALSE);
clock_state = data;
break;
case CMD_ENABLED:
PRINTM(MINFO, "SDIO clock is turned on\n");
ret = woal_sdio_set_bus_clock(priv->phandle, MTRUE);
clock_state = data;
break;
default:
ret = -EINVAL;
PRINTM(MINFO, "sdioclock: wrong parameter\n");
break;
}
done:
return ret;
}
/**
* @brief Set/Get beacon interval
*
* @param priv A pointer to moal_private structure
* @param wrq A pointer to iwreq structure
*
* @return 0 --success, otherwise fail
*/
static int
woal_beacon_interval(moal_private * priv, struct iwreq *wrq)
{
int ret = 0;
mlan_ds_bss *bss = NULL;
mlan_ioctl_req *req = NULL;
int bcn = 0;
ENTER();
if (wrq->u.data.length) {
if (copy_from_user(&bcn, wrq->u.data.pointer, sizeof(int))) {
PRINTM(MERROR, "Copy from user failed\n");
ret = -EFAULT;
goto done;
}
if ((bcn < MLAN_MIN_BEACON_INTERVAL) ||
(bcn > MLAN_MAX_BEACON_INTERVAL)) {
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;
bss->sub_command = MLAN_OID_IBSS_BCN_INTERVAL;
req->req_id = MLAN_IOCTL_BSS;
if (!wrq->u.data.length)
req->action = MLAN_ACT_GET;
else {
req->action = MLAN_ACT_SET;
bss->param.bcn_interval = bcn;
}
if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
ret = -EFAULT;
goto done;
}
if (copy_to_user
(wrq->u.data.pointer, (t_u8 *) & bss->param.bcn_interval,
sizeof(int))) {
ret = -EFAULT;
goto done;
}
wrq->u.data.length = 1;
done:
if (req)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief Set/Get ATIM window
*
* @param priv A pointer to moal_private structure
* @param wrq A pointer to iwreq structure
*
* @return 0 --success, otherwise fail
*/
static int
woal_atim_window(moal_private * priv, struct iwreq *wrq)
{
int ret = 0;
mlan_ds_bss *bss = NULL;
mlan_ioctl_req *req = NULL;
int atim = 0;
ENTER();
if (wrq->u.data.length) {
if (copy_from_user(&atim, wrq->u.data.pointer, sizeof(int))) {
PRINTM(MERROR, "Copy from user failed\n");
ret = -EFAULT;
goto done;
}
if ((atim < 0) || (atim > MLAN_MAX_ATIM_WINDOW)) {
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;
bss->sub_command = MLAN_OID_IBSS_ATIM_WINDOW;
req->req_id = MLAN_IOCTL_BSS;
if (!wrq->u.data.length)
req->action = MLAN_ACT_GET;
else {
req->action = MLAN_ACT_SET;
bss->param.atim_window = atim;
}
if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
ret = -EFAULT;
goto done;
}
if (copy_to_user
(wrq->u.data.pointer, (t_u8 *) & bss->param.atim_window, sizeof(int))) {
ret = -EFAULT;
goto done;
}
wrq->u.data.length = 1;
done:
if (req)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief Set/Get TX data rate
*
* @param priv A pointer to moal_private structure
* @param wrq A pointer to iwreq structure
*
* @return 0 --success, otherwise fail
*/
static int
woal_set_get_txrate(moal_private * priv, struct iwreq *wrq)
{
int ret = 0;
mlan_ds_rate *rate = NULL;
mlan_ioctl_req *req = NULL;
int rateindex = 0;
ENTER();
if (wrq->u.data.length) {
if (copy_from_user(&rateindex, wrq->u.data.pointer, sizeof(int))) {
PRINTM(MERROR, "Copy from user failed\n");
ret = -EFAULT;
goto done;
}
}
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_rate));
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
rate = (mlan_ds_rate *) req->pbuf;
rate->param.rate_cfg.rate_type = MLAN_RATE_INDEX;
rate->sub_command = MLAN_OID_RATE_CFG;
req->req_id = MLAN_IOCTL_RATE;
if (!wrq->u.data.length)
req->action = MLAN_ACT_GET;
else {
req->action = MLAN_ACT_SET;
if (rateindex == AUTO_RATE)
rate->param.rate_cfg.is_rate_auto = 1;
else {
if ((rateindex != MLAN_RATE_INDEX_MCS32) &&
((rateindex < 0) || (rateindex > MLAN_RATE_INDEX_MCS7))) {
ret = -EINVAL;
goto done;
}
}
rate->param.rate_cfg.rate = rateindex;
}
if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
ret = -EFAULT;
goto done;
} else {
if (wrq->u.data.length)
priv->rate_index = rateindex;
}
if (!wrq->u.data.length) {
if (rate->param.rate_cfg.is_rate_auto)
rateindex = AUTO_RATE;
else
rateindex = rate->param.rate_cfg.rate;
wrq->u.data.length = 1;
if (copy_to_user(wrq->u.data.pointer, &rateindex, sizeof(int))) {
ret = -EFAULT;
}
}
done:
if (req)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief Set/Get region code
*
* @param priv A pointer to moal_private structure
* @param wrq A pointer to iwreq structure
*
* @return 0 --success, otherwise fail
*/
static int
woal_set_get_regioncode(moal_private * priv, struct iwreq *wrq)
{
int ret = 0;
mlan_ds_misc_cfg *cfg = NULL;
mlan_ioctl_req *req = NULL;
int region = 0;
ENTER();
if (wrq->u.data.length) {
if (copy_from_user(&region, wrq->u.data.pointer, sizeof(int))) {
PRINTM(MERROR, "Copy from user failed\n");
ret = -EFAULT;
goto done;
}
}
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg));
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
cfg = (mlan_ds_misc_cfg *) req->pbuf;
cfg->sub_command = MLAN_OID_MISC_REGION;
req->req_id = MLAN_IOCTL_MISC_CFG;
if (!wrq->u.data.length)
req->action = MLAN_ACT_GET;
else {
req->action = MLAN_ACT_SET;
cfg->param.region_code = region;
}
if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
ret = -EFAULT;
goto done;
}
if (!wrq->u.data.length) {
wrq->u.data.length = 1;
if (copy_to_user
(wrq->u.data.pointer, &cfg->param.region_code, sizeof(int)))
ret = -EFAULT;
}
done:
if (req)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief Set/Get radio
*
* @param priv A pointer to moal_private structure
* @param wrq A pointer to iwreq structure
*
* @return 0 --success, otherwise fail
*/
static int
woal_set_get_radio(moal_private * priv, struct iwreq *wrq)
{
int ret = 0;
mlan_bss_info bss_info;
int option = 0;
ENTER();
memset(&bss_info, 0, sizeof(bss_info));
if (wrq->u.data.length) {
/* Set radio */
if (copy_from_user(&option, wrq->u.data.pointer, sizeof(int))) {
PRINTM(MERROR, "Copy from user failed\n");
ret = -EFAULT;
goto done;
}
if (MLAN_STATUS_SUCCESS != woal_set_radio(priv, (t_u8) option))
ret = -EFAULT;
} else {
/* Get radio status */
woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info);
wrq->u.data.length = 1;
if (copy_to_user
(wrq->u.data.pointer, &bss_info.radio_on,
sizeof(bss_info.radio_on))) {
PRINTM(MERROR, "Copy to user failed\n");
ret = -EFAULT;
}
}
done:
LEAVE();
return ret;
}
#ifdef DEBUG_LEVEL1
/**
* @brief Get/Set the bit mask of driver debug message control
*
* @param priv A pointer to moal_private structure
* @param wrq A pointer to wrq structure
*
* @return 0 --success, otherwise fail
*/
static int
woal_drv_dbg(moal_private * priv, struct iwreq *wrq)
{
int data[4];
int ret = 0;
ENTER();
if (!wrq->u.data.length) {
data[0] = drvdbg;
data[1] = ifdbg;
/* Return the current driver debug bit masks */
if (copy_to_user(wrq->u.data.pointer, data, sizeof(int) * 2)) {
PRINTM(MERROR, "Copy to user failed\n");
ret = -EFAULT;
goto drvdbgexit;
}
wrq->u.data.length = 2;
} else if (wrq->u.data.length < 3) {
/* Get the driver debug bit masks */
if (copy_from_user
(data, wrq->u.data.pointer, sizeof(int) * wrq->u.data.length)) {
PRINTM(MERROR, "Copy from user failed\n");
ret = -EFAULT;
goto drvdbgexit;
}
drvdbg = data[0];
if (wrq->u.data.length == 2)
ifdbg = data[1];
} else {
PRINTM(MERROR, "Invalid parameter number\n");
goto drvdbgexit;
}
printk(KERN_ALERT "drvdbg = 0x%08lx\n", drvdbg);
#ifdef DEBUG_LEVEL2
printk(KERN_ALERT "MINFO (%08lx) %s\n", MINFO,
(drvdbg & MINFO) ? "X" : "");
printk(KERN_ALERT "MWARN (%08lx) %s\n", MWARN,
(drvdbg & MWARN) ? "X" : "");
printk(KERN_ALERT "MENTRY (%08lx) %s\n", MENTRY,
(drvdbg & MENTRY) ? "X" : "");
#endif
printk(KERN_ALERT "MFW_D (%08lx) %s\n", MFW_D,
(drvdbg & MFW_D) ? "X" : "");
printk(KERN_ALERT "MCMD_D (%08lx) %s\n", MCMD_D,
(drvdbg & MCMD_D) ? "X" : "");
printk(KERN_ALERT "MDAT_D (%08lx) %s\n", MDAT_D,
(drvdbg & MDAT_D) ? "X" : "");
printk(KERN_ALERT "MINTR (%08lx) %s\n", MINTR,
(drvdbg & MINTR) ? "X" : "");
printk(KERN_ALERT "MEVENT (%08lx) %s\n", MEVENT,
(drvdbg & MEVENT) ? "X" : "");
printk(KERN_ALERT "MCMND (%08lx) %s\n", MCMND,
(drvdbg & MCMND) ? "X" : "");
printk(KERN_ALERT "MDATA (%08lx) %s\n", MDATA,
(drvdbg & MDATA) ? "X" : "");
printk(KERN_ALERT "MERROR (%08lx) %s\n", MERROR,
(drvdbg & MERROR) ? "X" : "");
printk(KERN_ALERT "MFATAL (%08lx) %s\n", MFATAL,
(drvdbg & MFATAL) ? "X" : "");
printk(KERN_ALERT "MMSG (%08lx) %s\n", MMSG, (drvdbg & MMSG) ? "X" : "");
printk(KERN_ALERT "ifdbg = 0x%08lx\n", ifdbg);
printk(KERN_ALERT "MIF_D (%08lx) %s\n", MIF_D, (ifdbg & MIF_D) ? "X" : "");
drvdbgexit:
LEAVE();
return ret;
}
#endif /* DEBUG_LEVEL1 */
/**
* @brief Set/Get Tx/Rx antenna
*
* @param priv A pointer to moal_private structure
* @param wrq A pointer to iwreq structure
*
* @return 0 --success, otherwise fail
*/
static int
woal_set_get_tx_rx_ant(moal_private * priv, struct iwreq *wrq)
{
int ret = 0;
mlan_ds_radio_cfg *radio = NULL;
mlan_ioctl_req *req = NULL;
int data = 0;
ENTER();
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_radio_cfg));
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
radio = (mlan_ds_radio_cfg *) req->pbuf;
radio->sub_command = MLAN_OID_ANT_CFG;
req->req_id = MLAN_IOCTL_RADIO_CFG;
if (wrq->u.data.length) {
if (copy_from_user(&data, wrq->u.data.pointer, sizeof(int))) {
PRINTM(MERROR, "Copy from user failed\n");
ret = -EFAULT;
goto done;
}
req->action = MLAN_ACT_SET;
radio->param.antenna = data;
} else
req->action = MLAN_ACT_GET;
if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
ret = -EFAULT;
goto done;
}
if (!wrq->u.data.length) {
data = radio->param.antenna;
if (copy_to_user(wrq->u.data.pointer, &data, sizeof(int))) {
ret = -EFAULT;
goto done;
}
wrq->u.data.length = 1;
}
done:
if (req)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief Set/Get QoS configuration
*
* @param priv A pointer to moal_private structure
* @param wrq A pointer to iwreq structure
*
* @return 0 --success, otherwise fail
*/
static int
woal_set_get_qos_cfg(moal_private * priv, struct iwreq *wrq)
{
int ret = 0;
mlan_ds_wmm_cfg *cfg = NULL;
mlan_ioctl_req *req = NULL;
int data = 0;
ENTER();
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_wmm_cfg));
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
cfg = (mlan_ds_wmm_cfg *) req->pbuf;
cfg->sub_command = MLAN_OID_WMM_CFG_QOS;
req->req_id = MLAN_IOCTL_WMM_CFG;
if (wrq->u.data.length) {
if (copy_from_user(&data, wrq->u.data.pointer, sizeof(int))) {
PRINTM(MERROR, "Copy from user failed\n");
ret = -EFAULT;
goto done;
}
req->action = MLAN_ACT_SET;
cfg->param.qos_cfg = (t_u8) data;
} else
req->action = MLAN_ACT_GET;
if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
ret = -EFAULT;
goto done;
}
if (!wrq->u.data.length) {
data = (int) cfg->param.qos_cfg;
if (copy_to_user(wrq->u.data.pointer, &data, sizeof(int))) {
ret = -EFAULT;
goto done;
}
wrq->u.data.length = 1;
}
done:
if (req)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief Set/Get LDO configuration
*
* @param priv A pointer to moal_private structure
* @param wrq A pointer to iwreq structure
*
* @return 0 --success, otherwise fail
*/
static int
woal_set_get_ldo_cfg(moal_private * priv, struct iwreq *wrq)
{
int ret = 0;
mlan_ds_misc_cfg *cfg = NULL;
mlan_ioctl_req *req = NULL;
int data = 0;
ENTER();
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg));
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
cfg = (mlan_ds_misc_cfg *) req->pbuf;
cfg->sub_command = MLAN_OID_MISC_LDO;
req->req_id = MLAN_IOCTL_MISC_CFG;
if (wrq->u.data.length) {
if (copy_from_user(&data, wrq->u.data.pointer, sizeof(int))) {
PRINTM(MERROR, "Copy from user failed\n");
ret = -EFAULT;
goto done;
}
if (data != LDO_INTERNAL && data != LDO_EXTERNAL) {
PRINTM(MERROR, "Invalid parameter, LDO config not changed\n");
ret = -EINVAL;
goto done;
}
req->action = MLAN_ACT_SET;
cfg->param.ldo_cfg = (t_u16) data;
} else
req->action = MLAN_ACT_GET;
if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
ret = -EFAULT;
goto done;
}
if (!wrq->u.data.length) {
data = (int) cfg->param.ldo_cfg;
if (copy_to_user(wrq->u.data.pointer, &data, sizeof(int))) {
ret = -EFAULT;
goto done;
}
wrq->u.data.length = 1;
}
done:
if (req)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief Set/Get WWS mode
*
* @param priv A pointer to moal_private structure
* @param wrq A pointer to iwreq structure
*
* @return 0 --success, otherwise fail
*/
static int
woal_wws_cfg(moal_private * priv, struct iwreq *wrq)
{
int ret = 0;
mlan_ds_misc_cfg *wws = NULL;
mlan_ioctl_req *req = NULL;
int data = 0;
ENTER();
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg));
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
wws = (mlan_ds_misc_cfg *) req->pbuf;
wws->sub_command = MLAN_OID_MISC_WWS;
req->req_id = MLAN_IOCTL_MISC_CFG;
if (wrq->u.data.length) {
if (copy_from_user(&data, wrq->u.data.pointer, sizeof(int))) {
PRINTM(MERROR, "Copy from user failed\n");
ret = -EFAULT;
goto done;
}
if (data != CMD_DISABLED && data != CMD_ENABLED) {
ret = -EINVAL;
goto done;
}
req->action = MLAN_ACT_SET;
wws->param.wws_cfg = data;
} else
req->action = MLAN_ACT_GET;
if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
ret = -EFAULT;
goto done;
}
if (!wrq->u.data.length) {
data = wws->param.wws_cfg;
if (copy_to_user(wrq->u.data.pointer, &data, sizeof(int))) {
ret = -EFAULT;
goto done;
}
wrq->u.data.length = 1;
}
done:
if (req)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief Set/Get sleep period
*
* @param priv A pointer to moal_private structure
* @param wrq A pointer to iwreq structure
*
* @return 0 --success, otherwise fail
*/
static int
woal_sleep_pd(moal_private * priv, struct iwreq *wrq)
{
int ret = 0;
mlan_ds_pm_cfg *pm_cfg = NULL;
mlan_ioctl_req *req = NULL;
int data = 0;
ENTER();
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_pm_cfg));
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
pm_cfg = (mlan_ds_pm_cfg *) req->pbuf;
pm_cfg->sub_command = MLAN_OID_PM_CFG_SLEEP_PD;
req->req_id = MLAN_IOCTL_PM_CFG;
if (wrq->u.data.length) {
if (copy_from_user(&data, wrq->u.data.pointer, sizeof(int))) {
PRINTM(MERROR, "Copy from user failed\n");
ret = -EFAULT;
goto done;
}
if ((data <= MAX_SLEEP_PERIOD && data >= MIN_SLEEP_PERIOD) ||
(data == 0)
|| (data == SLEEP_PERIOD_RESERVED_FF)
) {
req->action = MLAN_ACT_SET;
pm_cfg->param.sleep_period = data;
} else {
ret = -EINVAL;
goto done;
}
} else
req->action = MLAN_ACT_GET;
if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
ret = -EFAULT;
goto done;
}
if (!wrq->u.data.length) {
data = pm_cfg->param.sleep_period;
if (copy_to_user(wrq->u.data.pointer, &data, sizeof(int))) {
ret = -EFAULT;
goto done;
}
wrq->u.data.length = 1;
}
done:
if (req)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief Configuer sleep parameters
*
* @param priv A pointer to moal_private structure
* @param req A pointer to ifreq structure
*
* @return 0 --success, otherwise fail
*/
static int
woal_sleep_params_ioctl(moal_private * priv, struct iwreq *wrq)
{
int ret = 0;
mlan_ioctl_req *req = NULL;
mlan_ds_pm_cfg *pm = NULL;
mlan_ds_sleep_params *psleep_params = NULL;
int data[6] = { 0 }, i;
#ifdef DEBUG_LEVEL1
char err_str[][35] = { {"sleep clock error in ppm"},
{"wakeup offset in usec"},
{"clock stabilization time in usec"},
{"value of reserved for debug"}
};
#endif
ENTER();
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_pm_cfg));
if (req == NULL) {
LEAVE();
return -ENOMEM;
}
pm = (mlan_ds_pm_cfg *) req->pbuf;
pm->sub_command = MLAN_OID_PM_CFG_SLEEP_PARAMS;
req->req_id = MLAN_IOCTL_PM_CFG;
psleep_params = (pmlan_ds_sleep_params) & pm->param.sleep_params;
if (wrq->u.data.length == 0) {
req->action = MLAN_ACT_GET;
} else if (wrq->u.data.length == 6) {
if (copy_from_user(data, wrq->u.data.pointer, sizeof(int) *
wrq->u.data.length)) {
/* copy_from_user failed */
PRINTM(MERROR, "S_PARAMS: copy from user failed\n");
return -EINVAL;
}
#define MIN_VAL 0x0000
#define MAX_VAL 0xFFFF
for (i = 0; i < 6; i++) {
if ((i == 3) || (i == 4)) {
/* These two cases are handled below the loop */
continue;
}
if (data[i] < MIN_VAL || data[i] > MAX_VAL) {
PRINTM(MERROR, "Invalid %s (0-65535)!\n", err_str[i]);
ret = -EINVAL;
goto done;
}
}
if (data[3] < 0 || data[3] > 2) {
PRINTM(MERROR, "Invalid control periodic calibration (0-2)!\n");
ret = -EINVAL;
goto done;
}
if (data[4] < 0 || data[4] > 2) {
PRINTM(MERROR, "Invalid control of external sleep clock (0-2)!\n");
ret = -EINVAL;
goto done;
}
req->action = MLAN_ACT_SET;
psleep_params->error = data[0];
psleep_params->offset = data[1];
psleep_params->stable_time = data[2];
psleep_params->cal_control = data[3];
psleep_params->ext_sleep_clk = data[4];
psleep_params->reserved = data[5];
} else {
ret = -EINVAL;
goto done;
}
if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
ret = -EFAULT;
goto done;
}
data[0] = psleep_params->error;
data[1] = psleep_params->offset;
data[2] = psleep_params->stable_time;
data[3] = psleep_params->cal_control;
data[4] = psleep_params->ext_sleep_clk;
data[5] = psleep_params->reserved;
wrq->u.data.length = 6;
if (copy_to_user(wrq->u.data.pointer, data, sizeof(int) *
wrq->u.data.length)) {
PRINTM(MERROR, "QCONFIG: copy to user failed\n");
ret = -EFAULT;
goto done;
}
done:
if (req)
kfree(req);
LEAVE();
return ret;
}
#ifdef REASSOCIATION
/**
* @brief Set/Get reassociation settings
*
* @param priv A pointer to moal_private structure
* @param wrq A pointer to iwreq structure
*
* @return 0 --success, otherwise fail
*/
static int
woal_set_get_reassoc(moal_private * priv, struct iwreq *wrq)
{
moal_handle *handle = priv->phandle;
int ret = 0;
int data = 0;
ENTER();
if (wrq->u.data.length) {
if (copy_from_user(&data, wrq->u.data.pointer, sizeof(int))) {
PRINTM(MERROR, "Copy from user failed\n");
ret = -EFAULT;
goto done;
}
if (data == 0) {
handle->reassoc_on = MFALSE;
if (handle->is_reassoc_timer_set == MTRUE) {
woal_cancel_timer(&handle->reassoc_timer);
handle->is_reassoc_timer_set = MFALSE;
}
} else
handle->reassoc_on = MTRUE;
} else {
data = (int) handle->reassoc_on;
if (copy_to_user(wrq->u.data.pointer, &data, sizeof(int))) {
ret = -EFAULT;
goto done;
}
wrq->u.data.length = 1;
}
done:
LEAVE();
return ret;
}
#endif /* REASSOCIATION */
/**
* @brief implement WMM enable command
*
* @param priv Pointer to the moal_private driver data struct
* @param wrq Pointer to user data
*
* @return 0 --success, otherwise fail
*/
static int
woal_wmm_enable_ioctl(moal_private * priv, struct iwreq *wrq)
{
int ret = 0;
mlan_ds_wmm_cfg *wmm = NULL;
mlan_ioctl_req *req = NULL;
int data = 0;
ENTER();
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_wmm_cfg));
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
wmm = (mlan_ds_wmm_cfg *) req->pbuf;
req->req_id = MLAN_IOCTL_WMM_CFG;
wmm->sub_command = MLAN_OID_WMM_CFG_ENABLE;
if (wrq->u.data.length) {
/* Set WMM configuration */
if (copy_from_user(&data, wrq->u.data.pointer, sizeof(int))) {
PRINTM(MERROR, "Copy from user failed\n");
ret = -EFAULT;
goto done;
}
if ((data < CMD_DISABLED) || (data > CMD_ENABLED)) {
ret = -EINVAL;
goto done;
}
req->action = MLAN_ACT_SET;
if (data == CMD_DISABLED)
wmm->param.wmm_enable = MFALSE;
else
wmm->param.wmm_enable = MTRUE;
} else {
/* Get WMM status */
req->action = MLAN_ACT_GET;
}
if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
ret = -EFAULT;
goto done;
}
if (wrq->u.data.pointer) {
if (copy_to_user
(wrq->u.data.pointer, &wmm->param.wmm_enable,
sizeof(wmm->param.wmm_enable))) {
PRINTM(MERROR, "Copy to user failed\n");
ret = -EFAULT;
goto done;
}
wrq->u.data.length = 1;
}
done:
if (req)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief Implement 802.11D enable command
*
* @param priv Pointer to the moal_private driver data struct
* @param wrq Pointer to user data
*
* @return 0 --success, otherwise fail
*/
static int
woal_11d_enable_ioctl(moal_private * priv, struct iwreq *wrq)
{
int ret = 0;
mlan_ds_11d_cfg *pcfg_11d = NULL;
mlan_ioctl_req *req = NULL;
int data = 0;
ENTER();
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11d_cfg));
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
pcfg_11d = (mlan_ds_11d_cfg *) req->pbuf;
req->req_id = MLAN_IOCTL_11D_CFG;
pcfg_11d->sub_command = MLAN_OID_11D_CFG_ENABLE;
if (wrq->u.data.length) {
/* Set 11D configuration */
if (copy_from_user(&data, wrq->u.data.pointer, sizeof(int))) {
PRINTM(MERROR, "Copy from user failed\n");
ret = -EFAULT;
goto done;
}
if ((data < CMD_DISABLED) || (data > CMD_ENABLED)) {
ret = -EINVAL;
goto done;
}
if (data == CMD_DISABLED)
pcfg_11d->param.enable_11d = MFALSE;
else
pcfg_11d->param.enable_11d = MTRUE;
req->action = MLAN_ACT_SET;
} else {
req->action = MLAN_ACT_GET;
}
if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
ret = -EFAULT;
goto done;
}
if (wrq->u.data.pointer) {
if (copy_to_user
(wrq->u.data.pointer, &pcfg_11d->param.enable_11d,
sizeof(pcfg_11d->param.enable_11d))) {
PRINTM(MERROR, "Copy to user failed\n");
ret = -EFAULT;
goto done;
}
wrq->u.data.length = 1;
}
done:
if (req)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief Control WPS Session Enable/Disable
*
* @param priv Pointer to the moal_private driver data struct
* @param wrq Pointer to user data
*
* @return 0 --success, otherwise fail
*/
static int
woal_wps_cfg_ioctl(moal_private * priv, struct iwreq *wrq)
{
int ret = 0;
mlan_ds_wps_cfg *pwps = NULL;
mlan_ioctl_req *req = NULL;
char buf[8];
struct iwreq *wreq = (struct iwreq *) wrq;
ENTER();
PRINTM(MINFO, "WOAL_WPS_SESSION\n");
memset(buf, 0, sizeof(buf));
if (copy_from_user(buf, wreq->u.data.pointer,
MIN(sizeof(buf) - 1, wreq->u.data.length))) {
PRINTM(MERROR, "Copy from user failed\n");
ret = -EFAULT;
goto done;
}
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_wps_cfg));
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
pwps = (mlan_ds_wps_cfg *) req->pbuf;
req->req_id = MLAN_IOCTL_WPS_CFG;
req->action = MLAN_ACT_SET;
pwps->sub_command = MLAN_OID_WPS_CFG_SESSION;
if (buf[0] == 1)
pwps->param.wps_session = MLAN_WPS_CFG_SESSION_START;
else
pwps->param.wps_session = MLAN_WPS_CFG_SESSION_END;
if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
ret = -EFAULT;
goto done;
}
done:
if (req)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief Convert ascii string to Hex integer
*
* @param d A pointer to integer buf
* @param s A pointer to ascii string
* @param dlen The length o fascii string
*
* @return Number of integer
*/
static int
woal_ascii2hex(t_u8 * d, char *s, t_u32 dlen)
{
int i;
t_u8 n;
ENTER();
memset(d, 0x00, dlen);
for (i = 0; i < dlen * 2; i++) {
if ((s[i] >= 48) && (s[i] <= 57))
n = s[i] - 48;
else if ((s[i] >= 65) && (s[i] <= 70))
n = s[i] - 55;
else if ((s[i] >= 97) && (s[i] <= 102))
n = s[i] - 87;
else
break;
if (!(i % 2))
n = n * 16;
d[i / 2] += n;
}
LEAVE();
return i;
}
/**
* @brief Return hex value of a give character
*
* @param chr Character to be converted
*
* @return The converted chanrater if chr is a valid hex, else 0
*/
static int
woal_hexval(char chr)
{
ENTER();
if (chr >= '0' && chr <= '9')
return chr - '0';
if (chr >= 'A' && chr <= 'F')
return chr - 'A' + 10;
if (chr >= 'a' && chr <= 'f')
return chr - 'a' + 10;
LEAVE();
return 0;
}
/**
* @brief Return hex value of a given ascii string
*
* @param a String to be converted to ascii
*
* @return The converted chanrater if a is a valid hex, else 0
*/
static int
woal_atox(char *a)
{
int i = 0;
ENTER();
while (isxdigit(*a))
i = i * 16 + woal_hexval(*a++);
LEAVE();
return i;
}
/**
* @brief Extension of strsep lib command. This function will also take care
* escape character
*
* @param s A pointer to array of chars to process
* @param delim The delimiter character to end the string
* @param esc The escape character to ignore for delimiter
*
* @return Pointer to the seperated string if delim found, else NULL
*/
static char *
woal_strsep(char **s, char delim, char esc)
{
char *se = *s, *sb;
ENTER();
if (!(*s) || (*se == '\0')) {
LEAVE();
return NULL;
}
for (sb = *s; *sb != '\0'; ++sb) {
if (*sb == esc && *(sb + 1) == esc) {
/*
* We get a esc + esc seq then keep the one esc
* and chop off the other esc character
*/
memmove(sb, sb + 1, strlen(sb));
continue;
}
if (*sb == esc && *(sb + 1) == delim) {
/*
* We get a delim + esc seq then keep the delim
* and chop off the esc character
*/
memmove(sb, sb + 1, strlen(sb));
continue;
}
if (*sb == delim)
break;
}
if (*sb == '\0')
sb = NULL;
else
*sb++ = '\0';
*s = sb;
LEAVE();
return se;
}
/**
* @brief Convert mac address from string to t_u8 buffer.
*
* @param mac_addr The buffer to store the mac address in.
* @param buf The source of mac address which is a string.
*
* @return N/A
*/
static void
woal_mac2u8(t_u8 * mac_addr, char *buf)
{
char *begin = buf, *end;
int i;
ENTER();
for (i = 0; i < ETH_ALEN; ++i) {
end = woal_strsep(&begin, ':', '/');
if (end)
mac_addr[i] = woal_atox(end);
}
LEAVE();
}
/**
* @brief Set WPA passphrase and SSID
*
* @param priv A pointer to moal_private structure
* @param wrq A pointer to user data
*
* @return 0 --success, otherwise fail
*/
static int
woal_passphrase(moal_private * priv, struct iwreq *wrq)
{
t_u16 len = 0;
static char buf[256];
char *begin, *end, *opt;
int ret = 0, action = -1, i;
mlan_ds_sec_cfg *sec = NULL;
mlan_ioctl_req *req = NULL;
t_u8 zero_mac[] = { 0, 0, 0, 0, 0, 0 };
t_u8 *mac = NULL;
ENTER();
if (!wrq->u.data.length) {
PRINTM(MERROR, "Argument missing for setpassphrase\n");
ret = -EINVAL;
goto done;
}
if (copy_from_user(buf, wrq->u.data.pointer, wrq->u.data.length)) {
PRINTM(MERROR, "Copy from user failed\n");
ret = -EFAULT;
goto done;
}
buf[wrq->u.data.length] = '\0';
if (wrq->u.data.length <= 1) {
PRINTM(MERROR, "No valid arguments\n");
ret = -EINVAL;
goto done;
}
/* Parse the buf to get the cmd_action */
begin = buf;
end = woal_strsep(&begin, ';', '/');
if (end)
action = woal_atox(end);
if (action < 0 || action > 2 || end[1] != '\0') {
PRINTM(MERROR, "Invalid action argument %s\n", end);
ret = -EINVAL;
goto done;
}
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg));
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
sec = (mlan_ds_sec_cfg *) req->pbuf;
sec->sub_command = MLAN_OID_SEC_CFG_PASSPHRASE;
req->req_id = MLAN_IOCTL_SEC_CFG;
if (action == 0)
req->action = MLAN_ACT_GET;
else
req->action = MLAN_ACT_SET;
while (begin) {
end = woal_strsep(&begin, ';', '/');
opt = woal_strsep(&end, '=', '/');
if (!opt || !end || !end[0]) {
PRINTM(MERROR, "Invalid option\n");
ret = -EINVAL;
break;
} else if (!strnicmp(opt, "ssid", strlen(opt))) {
if (strlen(end) > MLAN_MAX_SSID_LENGTH) {
PRINTM(MERROR, "SSID length exceeds max length\n");
ret = -EFAULT;
break;
}
sec->param.passphrase.ssid.ssid_len = strlen(end);
strcpy((char *) sec->param.passphrase.ssid.ssid, end);
PRINTM(MINFO, "ssid=%s, len=%d\n", sec->param.passphrase.ssid.ssid,
(int) sec->param.passphrase.ssid.ssid_len);
} else if (!strnicmp(opt, "bssid", strlen(opt))) {
woal_mac2u8((t_u8 *) & sec->param.passphrase.bssid, end);
} else if (!strnicmp(opt, "psk", strlen(opt)) &&
req->action == MLAN_ACT_SET) {
if (strlen(end) != (MLAN_MAX_PMK_LENGTH * 2)) {
PRINTM(MERROR, "Invalid PMK length\n");
ret = -EINVAL;
break;
}
woal_ascii2hex(sec->param.passphrase.psk.pmk.pmk, end,
MLAN_MAX_PMK_LENGTH * 2);
sec->param.passphrase.psk_type = MLAN_PSK_PMK;
} else if (!strnicmp(opt, "passphrase", strlen(opt)) &&
req->action == MLAN_ACT_SET) {
if (strlen(end) < MLAN_MIN_PASSPHRASE_LENGTH ||
strlen(end) > MLAN_MAX_PASSPHRASE_LENGTH) {
PRINTM(MERROR, "Invalid length for passphrase\n");
ret = -EINVAL;
break;
}
sec->param.passphrase.psk_type = MLAN_PSK_PASSPHRASE;
strcpy(sec->param.passphrase.psk.passphrase.passphrase, end);
sec->param.passphrase.psk.passphrase.passphrase_len = strlen(end);
PRINTM(MINFO, "passphrase=%s, len=%d\n",
sec->param.passphrase.psk.passphrase.passphrase,
(int) sec->param.passphrase.psk.passphrase.passphrase_len);
} else {
PRINTM(MERROR, "Invalid option %s\n", opt);
ret = -EINVAL;
break;
}
}
if (ret)
goto done;
if (action == 2)
sec->param.passphrase.psk_type = MLAN_PSK_CLEAR;
if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
ret = -EFAULT;
goto done;
}
if (action == 0) {
memset(buf, 0, sizeof(buf));
if (sec->param.passphrase.ssid.ssid_len) {
len += sprintf(buf + len, "ssid:");
memcpy(buf + len, sec->param.passphrase.ssid.ssid,
sec->param.passphrase.ssid.ssid_len);
len += sec->param.passphrase.ssid.ssid_len;
len += sprintf(buf + len, " ");
}
if (memcmp(&sec->param.passphrase.bssid, zero_mac, sizeof(zero_mac))) {
mac = (t_u8 *) & sec->param.passphrase.bssid;
len += sprintf(buf + len, "bssid:");
for (i = 0; i < ETH_ALEN - 1; ++i)
len += sprintf(buf + len, "%02x:", mac[i]);
len += sprintf(buf + len, "%02x ", mac[i]);
}
if (sec->param.passphrase.psk_type == MLAN_PSK_PMK) {
len += sprintf(buf + len, "psk:");
for (i = 0; i < MLAN_MAX_PMK_LENGTH; ++i)
len +=
sprintf(buf + len, "%02x",
sec->param.passphrase.psk.pmk.pmk[i]);
len += sprintf(buf + len, "\n");
}
if (sec->param.passphrase.psk_type == MLAN_PSK_PASSPHRASE) {
len +=
sprintf(buf + len, "passphrase:%s \n",
sec->param.passphrase.psk.passphrase.passphrase);
}
if (wrq->u.data.pointer) {
if (copy_to_user(wrq->u.data.pointer, buf, len)) {
PRINTM(MERROR, "Copy to user failed, len %d\n", len);
ret = -EFAULT;
goto done;
}
wrq->u.data.length = len;
}
}
done:
if (req)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief Get esupp mode
*
* @param priv A pointer to moal_private structure
* @param wrq A pointer to iwreq structure
*
* @return 0 --success, otherwise fail
*/
static int
woal_get_esupp_mode(moal_private * priv, struct iwreq *wrq)
{
int ret = 0;
mlan_ds_sec_cfg *sec = NULL;
mlan_ioctl_req *req = NULL;
ENTER();
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg));
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
sec = (mlan_ds_sec_cfg *) req->pbuf;
sec->sub_command = MLAN_OID_SEC_CFG_ESUPP_MODE;
req->req_id = MLAN_IOCTL_SEC_CFG;
req->action = MLAN_ACT_GET;
if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
ret = -EFAULT;
goto done;
}
if (copy_to_user
(wrq->u.data.pointer, (t_u8 *) & sec->param.esupp_mode,
sizeof(int) * 3)) {
ret = -EFAULT;
goto done;
}
wrq->u.data.length = 3;
done:
if (req)
kfree(req);
LEAVE();
return ret;
}
/** AES key length */
#define AES_KEY_LEN 16
/**
* @brief Adhoc AES control
*
* @param priv A pointer to moal_private structure
* @param wrq A pointer to user data
*
* @return 0 --success, otherwise fail
*/
static int
woal_adhoc_aes_ioctl(moal_private * priv, struct iwreq *wrq)
{
static char buf[256];
int ret = 0, action = -1, i;
t_u8 key_ascii[32];
t_u8 key_hex[16];
t_u8 *tmp;
mlan_bss_info bss_info;
mlan_ds_sec_cfg *sec = NULL;
mlan_ioctl_req *req = NULL;
ENTER();
memset(key_ascii, 0x00, sizeof(key_ascii));
memset(key_hex, 0x00, sizeof(key_hex));
/* Get current BSS information */
memset(&bss_info, 0, sizeof(bss_info));
woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info);
if (bss_info.bss_mode != MLAN_BSS_MODE_IBSS ||
bss_info.media_connected == MTRUE) {
PRINTM(MERROR, "STA is connected or not in IBSS mode.\n");
ret = -EOPNOTSUPP;
goto done;
}
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg));
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
if (wrq->u.data.length) {
if (copy_from_user(buf, wrq->u.data.pointer, wrq->u.data.length)) {
PRINTM(MERROR, "Copy from user failed\n");
ret = -EFAULT;
goto done;
}
buf[wrq->u.data.length] = '\0';
if (wrq->u.data.length == 1) {
/* Get Adhoc AES Key */
req->req_id = MLAN_IOCTL_SEC_CFG;
req->action = MLAN_ACT_GET;
sec = (mlan_ds_sec_cfg *) req->pbuf;
sec->sub_command = MLAN_OID_SEC_CFG_ENCRYPT_KEY;
sec->param.encrypt_key.key_len = AES_KEY_LEN;
sec->param.encrypt_key.key_index = 0x40000000;
if (MLAN_STATUS_SUCCESS !=
woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
ret = -EFAULT;
goto done;
}
memcpy(key_hex, sec->param.encrypt_key.key_material,
sizeof(key_hex));
HEXDUMP("Adhoc AES Key (HEX)", key_hex, sizeof(key_hex));
wrq->u.data.length = sizeof(key_ascii) + 1;
tmp = key_ascii;
for (i = 0; i < sizeof(key_hex); i++)
tmp += sprintf((char *) tmp, "%02x", key_hex[i]);
} else if (wrq->u.data.length >= 2) {
/* Parse the buf to get the cmd_action */
action = woal_atox(&buf[0]);
if (action < 1 || action > 2) {
PRINTM(MERROR, "Invalid action argument %d\n", action);
ret = -EINVAL;
goto done;
}
req->req_id = MLAN_IOCTL_SEC_CFG;
req->action = MLAN_ACT_SET;
sec = (mlan_ds_sec_cfg *) req->pbuf;
sec->sub_command = MLAN_OID_SEC_CFG_ENCRYPT_KEY;
if (action == 1) {
/* Set Adhoc AES Key */
memcpy(key_ascii, &buf[2], sizeof(key_ascii));
woal_ascii2hex(key_hex, (char *) key_ascii, sizeof(key_hex));
HEXDUMP("Adhoc AES Key (HEX)", key_hex, sizeof(key_hex));
sec->param.encrypt_key.key_len = AES_KEY_LEN;
sec->param.encrypt_key.key_index = 0x40000000;
memcpy(sec->param.encrypt_key.key_material,
key_hex, sec->param.encrypt_key.key_len);
if (MLAN_STATUS_SUCCESS !=
woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
ret = -EFAULT;
goto done;
}
} else if (action == 2) {
/* Clear Adhoc AES Key */
sec->param.encrypt_key.key_len = AES_KEY_LEN;
sec->param.encrypt_key.key_index = 0x40000000;
memset(sec->param.encrypt_key.key_material, 0,
sizeof(sec->param.encrypt_key.key_material));
if (MLAN_STATUS_SUCCESS !=
woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
ret = -EFAULT;
goto done;
}
} else {
PRINTM(MERROR, "Invalid argument\n");
ret = -EINVAL;
goto done;
}
}
HEXDUMP("Adhoc AES Key (ASCII)", key_ascii, sizeof(key_ascii));
wrq->u.data.length = sizeof(key_ascii);
if (wrq->u.data.pointer) {
if (copy_to_user(wrq->u.data.pointer, &key_ascii,
sizeof(key_ascii))) {
PRINTM(MERROR, "copy_to_user failed\n");
ret = -EFAULT;
goto done;
}
}
}
done:
if (req)
kfree(req);
LEAVE();
return ret;
}
#ifdef MFG_CMD_SUPPORT
/**
* @brief Manufacturing command ioctl function
*
* @param priv A pointer to moal_private structure
* @param wrq A pointer to iwreq structure
* @return 0 --success, otherwise fail
*/
static int
woal_mfg_command(moal_private * priv, struct iwreq *wrq)
{
HostCmd_Header cmd_header;
int ret = 0;
mlan_ds_misc_cfg *misc = NULL;
mlan_ioctl_req *req = NULL;
ENTER();
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg));
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
misc = (mlan_ds_misc_cfg *) req->pbuf;
memset(&cmd_header, 0, sizeof(cmd_header));
/* get MFG command header */
if (copy_from_user
(&cmd_header, wrq->u.data.pointer, sizeof(HostCmd_Header))) {
PRINTM(MERROR, "copy from user failed: MFG command header\n");
ret = -EFAULT;
goto done;
}
misc->param.mfgcmd.len = cmd_header.size;
PRINTM(MINFO, "MFG command len = %lu\n", misc->param.mfgcmd.len);
if (cmd_header.size > MLAN_SIZE_OF_CMD_BUFFER) {
ret = -EINVAL;
goto done;
}
/* get the whole command from user */
if (copy_from_user
(misc->param.mfgcmd.cmd, wrq->u.data.pointer, cmd_header.size)) {
PRINTM(MERROR, "copy from user failed: MFG command\n");
ret = -EFAULT;
goto done;
}
misc->sub_command = MLAN_OID_MISC_MFG_CMD;
req->req_id = MLAN_IOCTL_MISC_CFG;
req->action = MLAN_ACT_SET;
if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
ret = -EFAULT;
goto done;
}
if (copy_to_user
(wrq->u.data.pointer, (t_u8 *) misc->param.mfgcmd.cmd,
MIN(cmd_header.size, misc->param.mfgcmd.len))) {
ret = -EFAULT;
goto done;
}
wrq->u.data.length = MIN(cmd_header.size, misc->param.mfgcmd.len);
done:
if (req)
kfree(req);
LEAVE();
return ret;
}
#endif /* MFG_CMD_SUPPORT */
/**
* @brief host command ioctl function
*
* @param priv A pointer to moal_private structure
* @param wrq A pointer to iwreq structure
* @return 0 --success, otherwise fail
*/
static int
woal_host_command(moal_private * priv, struct iwreq *wrq)
{
HostCmd_Header cmd_header;
int ret = 0;
mlan_ds_misc_cfg *misc = NULL;
mlan_ioctl_req *req = NULL;
ENTER();
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg));
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
misc = (mlan_ds_misc_cfg *) req->pbuf;
memset(&cmd_header, 0, sizeof(cmd_header));
/* get command header */
if (copy_from_user
(&cmd_header, wrq->u.data.pointer, sizeof(HostCmd_Header))) {
PRINTM(MERROR, "copy from user failed: Host command header\n");
ret = -EFAULT;
goto done;
}
misc->param.hostcmd.len = woal_le16_to_cpu(cmd_header.size);
PRINTM(MINFO, "Host command len = %lu\n", misc->param.hostcmd.len);
if (woal_le16_to_cpu(cmd_header.size) > MLAN_SIZE_OF_CMD_BUFFER) {
ret = -EINVAL;
goto done;
}
/* get the whole command from user */
if (copy_from_user
(misc->param.hostcmd.cmd, wrq->u.data.pointer,
woal_le16_to_cpu(cmd_header.size))) {
PRINTM(MERROR, "copy from user failed\n");
ret = -EFAULT;
goto done;
}
misc->sub_command = MLAN_OID_MISC_HOST_CMD;
req->req_id = MLAN_IOCTL_MISC_CFG;
if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
ret = -EFAULT;
goto done;
}
if (copy_to_user
(wrq->u.data.pointer, (t_u8 *) misc->param.hostcmd.cmd,
misc->param.hostcmd.len)) {
ret = -EFAULT;
goto done;
}
wrq->u.data.length = misc->param.hostcmd.len;
done:
if (req)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief arpfilter ioctl function
*
* @param priv A pointer to moal_private structure
* @param wrq A pointer to iwreq structure
* @return 0 --success, otherwise fail
*/
static int
woal_arp_filter(moal_private * priv, struct iwreq *wrq)
{
int ret = 0;
mlan_ds_misc_cfg *misc = NULL;
mlan_ioctl_req *req = NULL;
ENTER();
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg));
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
misc = (mlan_ds_misc_cfg *) req->pbuf;
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_ARP_FILTER;
misc->param.gen_ie.len = wrq->u.data.length;
/* get the whole command from user */
if (copy_from_user
(misc->param.gen_ie.ie_data, wrq->u.data.pointer, wrq->u.data.length)) {
PRINTM(MERROR, "copy from user failed\n");
ret = -EFAULT;
goto done;
}
if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
ret = -EFAULT;
goto done;
}
done:
if (req)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief Create a brief scan resp to relay basic BSS info to the app layer
*
* When the beacon/probe response has not been buffered, use the saved BSS
* information available to provide a minimum response for the application
* ioctl retrieval routines. Include:
* - Timestamp
* - Beacon Period
* - Capabilities (including WMM Element if available)
* - SSID
*
* @param ppbuffer Output parameter: Buffer used to create basic scan rsp
* @param pbss_desc Pointer to a BSS entry in the scan table to create
* scan response from for delivery to the application layer
*
* @return void
*/
static void
wlan_scan_create_brief_table_entry(t_u8 ** ppbuffer,
BSSDescriptor_t * pbss_desc)
{
t_u8 *ptmp_buf = *ppbuffer;
t_u8 tmp_ssid_hdr[2];
t_u8 ie_len = 0;
if (copy_to_user(ptmp_buf, pbss_desc->time_stamp,
sizeof(pbss_desc->time_stamp))) {
PRINTM(MINFO, "Copy to user failed\n");
return;
}
ptmp_buf += sizeof(pbss_desc->time_stamp);
if (copy_to_user(ptmp_buf, &pbss_desc->beacon_period,
sizeof(pbss_desc->beacon_period))) {
PRINTM(MINFO, "Copy to user failed\n");
return;
}
ptmp_buf += sizeof(pbss_desc->beacon_period);
if (copy_to_user
(ptmp_buf, &pbss_desc->cap_info, sizeof(pbss_desc->cap_info))) {
PRINTM(MINFO, "Copy to user failed\n");
return;
}
ptmp_buf += sizeof(pbss_desc->cap_info);
tmp_ssid_hdr[0] = 0; /* Element ID for SSID is zero */
tmp_ssid_hdr[1] = pbss_desc->ssid.ssid_len;
if (copy_to_user(ptmp_buf, tmp_ssid_hdr, sizeof(tmp_ssid_hdr))) {
PRINTM(MINFO, "Copy to user failed\n");
return;
}
ptmp_buf += sizeof(tmp_ssid_hdr);
if (copy_to_user(ptmp_buf, pbss_desc->ssid.ssid, pbss_desc->ssid.ssid_len)) {
PRINTM(MINFO, "Copy to user failed\n");
return;
}
ptmp_buf += pbss_desc->ssid.ssid_len;
if (pbss_desc->wmm_ie.vend_hdr.element_id == WMM_IE) {
ie_len = sizeof(IEEEtypes_Header_t) + pbss_desc->wmm_ie.vend_hdr.len;
if (copy_to_user(ptmp_buf, &pbss_desc->wmm_ie, ie_len)) {
PRINTM(MINFO, "Copy to user failed\n");
return;
}
ptmp_buf += ie_len;
}
if (pbss_desc->pwpa_ie) {
if ((*(pbss_desc->pwpa_ie)).vend_hdr.element_id == WPA_IE) {
ie_len =
sizeof(IEEEtypes_Header_t) +
(*(pbss_desc->pwpa_ie)).vend_hdr.len;
if (copy_to_user(ptmp_buf, pbss_desc->pwpa_ie, ie_len)) {
PRINTM(MINFO, "Copy to user failed\n");
return;
}
}
ptmp_buf += ie_len;
}
if (pbss_desc->prsn_ie) {
if ((*(pbss_desc->prsn_ie)).ieee_hdr.element_id == RSN_IE) {
ie_len =
sizeof(IEEEtypes_Header_t) +
(*(pbss_desc->prsn_ie)).ieee_hdr.len;
if (copy_to_user(ptmp_buf, pbss_desc->prsn_ie, ie_len)) {
PRINTM(MINFO, "Copy to user failed\n");
return;
}
}
ptmp_buf += ie_len;
}
*ppbuffer = ptmp_buf;
}
/**
* @brief Create a wlan_ioctl_get_scan_table_entry for a given BSS
* Descriptor for inclusion in the ioctl response to the user space
* application.
*
*
* @param pbss_desc Pointer to a BSS entry in the scan table to form
* scan response from for delivery to the application layer
* @param ppbuffer Output parameter: Buffer used to output scan return struct
* @param pspace_left Output parameter: Number of bytes available in the
* response buffer.
*
* @return MLAN_STATUS_SUCCESS, or < 0 with IOCTL error code
*/
static int
wlan_get_scan_table_ret_entry(BSSDescriptor_t * pbss_desc,
t_u8 ** ppbuffer, int *pspace_left)
{
wlan_ioctl_get_scan_table_entry *prsp_entry;
wlan_ioctl_get_scan_table_entry tmp_rsp_entry;
int space_needed;
t_u8 *pcurrent;
int variable_size;
const int fixed_size = (sizeof(tmp_rsp_entry.fixed_field_length)
+ sizeof(tmp_rsp_entry.fixed_fields)
+ sizeof(tmp_rsp_entry.bss_info_length));
ENTER();
pcurrent = *ppbuffer;
/* The variable size returned is the stored beacon size */
variable_size = pbss_desc->beacon_buf_size;
/* If we stored a beacon and its size was zero, set the variable size
return value to the size of the brief scan response
wlan_scan_create_brief_table_entry creates. Also used if we are not
configured to store beacons in the first place */
if (!variable_size) {
variable_size = pbss_desc->ssid.ssid_len + 2;
variable_size += (sizeof(pbss_desc->beacon_period)
+ sizeof(pbss_desc->time_stamp)
+ sizeof(pbss_desc->cap_info));
if (pbss_desc->wmm_ie.vend_hdr.element_id == WMM_IE) {
variable_size += (sizeof(IEEEtypes_Header_t)
+ pbss_desc->wmm_ie.vend_hdr.len);
}
if (pbss_desc->pwpa_ie) {
if ((*(pbss_desc->pwpa_ie)).vend_hdr.element_id == WPA_IE) {
variable_size += (sizeof(IEEEtypes_Header_t)
+ (*(pbss_desc->pwpa_ie)).vend_hdr.len);
}
}
if (pbss_desc->prsn_ie) {
if ((*(pbss_desc->prsn_ie)).ieee_hdr.element_id == RSN_IE) {
variable_size += (sizeof(IEEEtypes_Header_t)
+ (*(pbss_desc->prsn_ie)).ieee_hdr.len);
}
}
}
space_needed = fixed_size + variable_size;
PRINTM(MINFO, "GetScanTable: need(%d), left(%d)\n",
space_needed, *pspace_left);
if (space_needed >= *pspace_left) {
*pspace_left = 0;
LEAVE();
return -E2BIG;
}
*pspace_left -= space_needed;
tmp_rsp_entry.fixed_field_length = sizeof(prsp_entry->fixed_fields);
memcpy(tmp_rsp_entry.fixed_fields.bssid,
pbss_desc->mac_address, sizeof(prsp_entry->fixed_fields.bssid));
tmp_rsp_entry.fixed_fields.rssi = pbss_desc->rssi;
tmp_rsp_entry.fixed_fields.channel = pbss_desc->channel;
tmp_rsp_entry.fixed_fields.network_tsf = pbss_desc->network_tsf;
tmp_rsp_entry.bss_info_length = variable_size;
/*
* Copy fixed fields to user space
*/
if (copy_to_user(pcurrent, &tmp_rsp_entry, fixed_size)) {
PRINTM(MINFO, "Copy to user failed\n");
LEAVE();
return -EFAULT;
}
pcurrent += fixed_size;
if (pbss_desc->pbeacon_buf) {
/*
* Copy variable length elements to user space
*/
if (copy_to_user(pcurrent, pbss_desc->pbeacon_buf,
pbss_desc->beacon_buf_size)) {
PRINTM(MINFO, "Copy to user failed\n");
LEAVE();
return -EFAULT;
}
pcurrent += pbss_desc->beacon_buf_size;
} else {
wlan_scan_create_brief_table_entry(&pcurrent, pbss_desc);
}
*ppbuffer = pcurrent;
LEAVE();
return MLAN_STATUS_SUCCESS;
}
/**
* @brief Retrieve the scan response/beacon table
*
* @param wrq A pointer to iwreq structure
* @param scan_resp A pointer to mlan_scan_resp structure
* @param scan_start argument
*
* @return MLAN_STATUS_SUCCESS --success, otherwise fail
*/
static int
moal_ret_get_scan_table_ioctl(struct iwreq *wrq, mlan_scan_resp * scan_resp,
t_u32 scan_start)
{
pBSSDescriptor_t pbss_desc, scan_table;
mlan_scan_resp *prsp_info;
int ret_code;
int ret_len;
int space_left;
t_u8 *pcurrent;
t_u8 *pbuffer_end;
t_u32 num_scans_done;
ENTER();
num_scans_done = 0;
ret_code = MLAN_STATUS_SUCCESS;
prsp_info = (mlan_scan_resp *) wrq->u.data.pointer;
prsp_info->pscan_table =
(t_u8 *) prsp_info + sizeof(prsp_info->num_in_scan_table);
pcurrent = prsp_info->pscan_table;
pbuffer_end = wrq->u.data.pointer + wrq->u.data.length - 1;
space_left = pbuffer_end - pcurrent;
scan_table = (BSSDescriptor_t *) (scan_resp->pscan_table);
PRINTM(MINFO, "GetScanTable: scan_start req = %ld\n", scan_start);
PRINTM(MINFO, "GetScanTable: length avail = %d\n", wrq->u.data.length);
if (!scan_start) {
PRINTM(MINFO, "GetScanTable: get current BSS Descriptor\n");
/* Use to get current association saved descriptor */
pbss_desc = scan_table;
ret_code = wlan_get_scan_table_ret_entry(pbss_desc,
&pcurrent, &space_left);
if (ret_code == MLAN_STATUS_SUCCESS) {
num_scans_done = 1;
}
} else {
scan_start--;
while (space_left
&& (scan_start + num_scans_done < scan_resp->num_in_scan_table)
&& (ret_code == MLAN_STATUS_SUCCESS)) {
pbss_desc = (scan_table + (scan_start + num_scans_done));
PRINTM(MINFO, "GetScanTable: get current BSS Descriptor [%ld]\n",
scan_start + num_scans_done);
ret_code = wlan_get_scan_table_ret_entry(pbss_desc,
&pcurrent, &space_left);
if (ret_code == MLAN_STATUS_SUCCESS) {
num_scans_done++;
}
}
}
prsp_info->num_in_scan_table = num_scans_done;
ret_len = pcurrent - (t_u8 *) wrq->u.data.pointer;
wrq->u.data.length = ret_len;
/* Return ret_code (EFAULT or E2BIG) in the case where no scan results were
successfully encoded. */
LEAVE();
return (num_scans_done ? MLAN_STATUS_SUCCESS : ret_code);
}
/**
* @brief Get scan table ioctl
*
* @param priv A pointer to moal_private structure
* @param wrq A pointer to iwreq structure
*
* @return MLAN_STATUS_SUCCESS -- success, otherwise fail
*/
static mlan_status
woal_get_scan_table_ioctl(moal_private * priv, struct iwreq *wrq)
{
int ret = 0;
mlan_ioctl_req *req = NULL;
mlan_ds_scan *scan = NULL;
t_u32 scan_start;
mlan_status status = MLAN_STATUS_SUCCESS;
ENTER();
/* Allocate an IOCTL request buffer */
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_scan));
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
/* Fill request buffer */
scan = (mlan_ds_scan *) req->pbuf;
req->req_id = MLAN_IOCTL_SCAN;
req->action = MLAN_ACT_GET;
/* get the whole command from user */
if (copy_from_user(&scan_start, wrq->u.data.pointer, sizeof(scan_start))) {
PRINTM(MERROR, "copy from user failed\n");
ret = -EFAULT;
goto done;
}
if (scan_start) {
scan->sub_command = MLAN_OID_SCAN_NORMAL;
} else {
scan->sub_command = MLAN_OID_SCAN_SPECIFIC_SSID;
}
/* Send IOCTL request to MLAN */
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
if (status == MLAN_STATUS_SUCCESS) {
status = moal_ret_get_scan_table_ioctl(wrq,
&scan->param.scan_resp,
scan_start);
}
done:
if (req && (status != MLAN_STATUS_PENDING))
kfree(req);
LEAVE();
return status;
}
/**
* @brief Set user scan
*
* @param priv A pointer to moal_private structure
* @param wrq A pointer to iwreq structure
*
* @return MLAN_STATUS_SUCCESS -- success, otherwise fail
*/
static mlan_status
woal_set_user_scan_ioctl(moal_private * priv, struct iwreq *wrq)
{
int ret = 0;
mlan_ioctl_req *req = NULL;
mlan_ds_scan *scan = NULL;
union iwreq_data wrqu;
mlan_status status = MLAN_STATUS_SUCCESS;
ENTER();
/* Allocate an IOCTL request buffer */
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_scan) + wrq->u.data.length);
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
/* Fill request buffer */
scan = (mlan_ds_scan *) req->pbuf;
scan->sub_command = MLAN_OID_SCAN_USER_CONFIG;
req->req_id = MLAN_IOCTL_SCAN;
req->action = MLAN_ACT_SET;
if (copy_from_user(scan->param.user_scan.scan_cfg_buf,
wrq->u.data.pointer, wrq->u.data.length)) {
PRINTM(MINFO, "Copy from user failed\n");
LEAVE();
return -EFAULT;
}
/* Send IOCTL request to MLAN */
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
if (status == MLAN_STATUS_SUCCESS) {
memset(&wrqu, 0, sizeof(union iwreq_data));
wireless_send_event(priv->netdev, SIOCGIWSCAN, &wrqu, NULL);
} else {
}
done:
if (req && (status != MLAN_STATUS_PENDING))
kfree(req);
LEAVE();
return status;
}
/**
* @brief Cmd52 read/write register
*
* @param priv A pointer to moal_private structure
* @param wrq A pointer to iwreq structure
* @return MLAN_STATUS_SUCCESS --success, otherwise fail
*/
static int
woal_cmd52rdwr_ioctl(moal_private * priv, struct iwreq *wrq)
{
t_u8 buf[7];
t_u8 rw, func, data = 0;
int reg, ret = MLAN_STATUS_SUCCESS;
ENTER();
if (copy_from_user(buf, wrq->u.data.pointer,
MIN(wrq->u.data.length, sizeof(buf)))) {
PRINTM(MINFO, "Copy from user failed\n");
ret = -EFAULT;
goto done;
}
rw = buf[0];
func = buf[1];
reg = buf[5];
reg = (reg << 8) + buf[4];
reg = (reg << 8) + buf[3];
reg = (reg << 8) + buf[2];
if (rw)
data = buf[6];
PRINTM(MINFO, "rw=%d, func=%d, reg=0x%08X, data=0x%02X\n", rw, func, reg,
data);
if (!rw) {
sdio_claim_host(((struct sdio_mmc_card *) priv->phandle->card)->func);
data =
sdio_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 {
sdio_claim_host(((struct sdio_mmc_card *) priv->phandle->card)->func);
sdio_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;
}
}
if (copy_to_user(wrq->u.data.pointer, &data, sizeof(data))) {
PRINTM(MINFO, "Copy to user failed\n");
ret = -EFAULT;
goto done;
}
done:
LEAVE();
return ret;
}
/**
* @brief Cmd53 read/write register
*
* @param priv A pointer to moal_private structure
* @param wrq A pointer to iwreq structure
* @return MLAN_STATUS_SUCCESS --success, otherwise fail
*/
static int
woal_cmd53rdwr_ioctl(moal_private * priv, struct iwreq *wrq)
{
t_u8 *buf = NULL;
t_u8 rw, func, mode;
t_u16 blklen = 0, blknum = 0;
int reg = 0, pattern_len = 0, total_len = 0, pos = 0, ret =
MLAN_STATUS_SUCCESS;
t_u8 *data = NULL;
ENTER();
if (!(buf = (t_u8 *) kmalloc(WOAL_2K_BYTES, GFP_ATOMIC))) {
PRINTM(MERROR, "Cannot allocate buffer for command!\n");
ret = -EFAULT;
goto done;
}
if (!(data = (t_u8 *) kmalloc(WOAL_2K_BYTES, GFP_ATOMIC))) {
PRINTM(MERROR, "Cannot allocate buffer for command!\n");
ret = -EFAULT;
goto done;
}
if (wrq->u.data.length > WOAL_2K_BYTES) {
PRINTM(MERROR, "Data lengh is too large!\n");
ret = -EINVAL;
goto done;
}
if (copy_from_user(buf, wrq->u.data.pointer, wrq->u.data.length)) {
PRINTM(MINFO, "Copy from user failed\n");
ret = -EFAULT;
goto done;
}
rw = buf[0]; /* read/write (0/1) */
func = buf[1]; /* func (0/1/2) */
reg = buf[5]; /* address */
reg = (reg << 8) + buf[4];
reg = (reg << 8) + buf[3];
reg = (reg << 8) + buf[2];
mode = buf[6]; /* byte mode/block mode (0/1) */
blklen = buf[8]; /* block size */
blklen = (blklen << 8) + buf[7];
blknum = buf[10]; /* block number or byte number */
blknum = (blknum << 8) + buf[9];
if (mode != BYTE_MODE)
mode = BLOCK_MODE;
total_len = (mode == BLOCK_MODE) ? blknum * blklen : blknum;
if (total_len > WOAL_2K_BYTES) {
PRINTM(MERROR, "Total data length is too large!\n");
ret = -EINVAL;
goto done;
}
PRINTM(MINFO, "CMD53 read/write, func = %d, addr = %#x, mode = %d, "
"block size = %d, block(byte) number = %d\n",
func, reg, mode, blklen, blknum);
if (!rw) {
sdio_claim_host(((struct sdio_mmc_card *) priv->phandle->card)->func);
if (!sdio_readsb
(((struct sdio_mmc_card *) priv->phandle->card)->func, data, reg,
total_len))
PRINTM(MERROR, "sdio_readsb: reading memory 0x%x failed\n", reg);
sdio_release_host(((struct sdio_mmc_card *) priv->phandle->card)->func);
if (copy_to_user(wrq->u.data.pointer, data, sizeof(data))) {
PRINTM(MINFO, "Copy to user failed\n");
ret = -EFAULT;
goto done;
}
} else {
pattern_len = wrq->u.data.length - 11;
if (pattern_len > total_len)
pattern_len = total_len;
memset(data, 0, sizeof(data));
/* Copy/duplicate the pattern to data buffer */
for (pos = 0; pos < total_len; pos++)
data[pos] = buf[11 + (pos % pattern_len)];
sdio_claim_host(((struct sdio_mmc_card *) priv->phandle->card)->func);
if (!sdio_writesb
(((struct sdio_mmc_card *) priv->phandle->card)->func, reg, data,
total_len))
PRINTM(MERROR, "sdio_writesb: writing memory 0x%x failed\n", reg);
sdio_release_host(((struct sdio_mmc_card *) priv->phandle->card)->func);
}
done:
if (buf)
kfree(buf);
if (data)
kfree(data);
LEAVE();
return ret;
}
#if defined(SDIO_MULTI_PORT_TX_AGGR) || defined(SDIO_MULTI_PORT_RX_AGGR)
/**
* @brief Set SDIO Multi-point aggregation control parameters
*
* @param priv A pointer to moal_private structure
* @param wrq A pointer to iwreq structure
*
* @return 0 --success, otherwise fail
*/
static int
woal_do_sdio_mpa_ctrl(moal_private * priv, struct iwreq *wrq)
{
int data[6];
int ret = 0;
mlan_ds_misc_cfg *misc = NULL;
mlan_ioctl_req *req = NULL;
ENTER();
PRINTM(MERROR, "mpa_ctrl: %d\n", wrq->u.data.length);
if (wrq->u.data.length > 6) {
PRINTM(MERROR, "Invalid number of arguments\n");
ret = -EINVAL;
goto done;
}
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;
memset(misc, 0, sizeof(mlan_ds_misc_cfg));
misc->sub_command = MLAN_OID_MISC_SDIO_MPA_CTRL;
req->req_id = MLAN_IOCTL_MISC_CFG;
/* Get the values first, then modify these values if user had modified them
*/
req->action = MLAN_ACT_GET;
if ((ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) !=
MLAN_STATUS_SUCCESS) {
PRINTM(MERROR, "woal_request_ioctl returned %d\n", ret);
ret = -EFAULT;
goto done;
}
if (wrq->u.data.length == 0) {
data[0] = misc->param.mpa_ctrl.tx_enable;
data[1] = misc->param.mpa_ctrl.rx_enable;
data[2] = misc->param.mpa_ctrl.tx_buf_size;
data[3] = misc->param.mpa_ctrl.rx_buf_size;
data[4] = misc->param.mpa_ctrl.tx_max_ports;
data[5] = misc->param.mpa_ctrl.rx_max_ports;
PRINTM(MINFO, "Get Param: %d %d %d %d %d %d\n", data[0], data[1],
data[2], data[3], data[4], data[5]);
if (copy_to_user(wrq->u.data.pointer, data, sizeof(data))) {
ret = -EFAULT;
goto done;
}
wrq->u.data.length = sizeof(data) / sizeof(int);
goto done;
}
if (copy_from_user(data, wrq->u.data.pointer,
sizeof(int) * wrq->u.data.length)) {
PRINTM(MINFO, "Copy from user failed\n");
ret = -EFAULT;
goto done;
}
switch (wrq->u.data.length) {
case 6:
misc->param.mpa_ctrl.rx_max_ports = data[5];
case 5:
misc->param.mpa_ctrl.tx_max_ports = data[4];
case 4:
misc->param.mpa_ctrl.rx_buf_size = data[3];
case 3:
misc->param.mpa_ctrl.tx_buf_size = data[2];
case 2:
misc->param.mpa_ctrl.rx_enable = data[1];
case 1:
/* Set cmd */
req->action = MLAN_ACT_SET;
PRINTM(MINFO, "Set Param: %d %d %d %d %d %d\n", data[0], data[1],
data[2], data[3], data[4], data[5]);
misc->param.mpa_ctrl.tx_enable = data[0];
break;
default:
PRINTM(MERROR, "Default case error\n");
ret = -EINVAL;
goto done;
}
if ((ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT) !=
MLAN_STATUS_SUCCESS)) {
PRINTM(MERROR, "woal_request_ioctl returned %d\n", ret);
ret = -EFAULT;
goto done;
}
done:
if (req)
kfree(req);
LEAVE();
return ret;
}
#endif /* SDIO_MULTI_PORT_TX_AGGR || SDIO_MULTI_PORT_RX_AGGR */
/**
* @brief Set/get TID multi-port eligibility table
*
* @param priv A pointer to moal_private structure
* @param wrq A pointer to iwreq structure
*
* @return 0 --success, otherwise fail
*/
static int
woal_set_get_tid_elig_tbl(moal_private * priv, struct iwreq *wrq)
{
int data[8] = { 0 }, ret = 0;
mlan_ds_wmm_cfg *cfg = NULL;
mlan_ioctl_req *req = NULL;
ENTER();
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_wmm_cfg));
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
if (wrq->u.data.length > 8) {
ret = -EINVAL;
goto done;
}
cfg = (mlan_ds_wmm_cfg *) req->pbuf;
cfg->sub_command = MLAN_OID_TID_ELIG_TBL;
req->req_id = MLAN_IOCTL_WMM_CFG;
if (wrq->u.data.length) {
if (copy_from_user
(data, wrq->u.data.pointer, (wrq->u.data.length * sizeof(int)))) {
PRINTM(MERROR, "Copy from user failed\n");
ret = -EFAULT;
goto done;
}
req->action = MLAN_ACT_SET;
memcpy(cfg->param.tid_tbl, (t_u32 *) data, sizeof(data));
} else
req->action = MLAN_ACT_GET;
if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
ret = -EFAULT;
goto done;
}
if (!wrq->u.data.length) {
memcpy(data, (int *) cfg->param.tid_tbl, sizeof(data));
if (copy_to_user(wrq->u.data.pointer, data, sizeof(data))) {
ret = -EFAULT;
goto done;
}
wrq->u.data.length = sizeof(data) / sizeof(int);
}
done:
if (req)
kfree(req);
LEAVE();
return ret;
}
/** Maximum number of probes to send on each channel */
#define MAX_PROBES 10
/**
* @brief Set/Get scan configuration parameters
*
* @param priv A pointer to moal_private structure
* @param wrq A pointer to iwreq structure
*
* @return 0 --success, otherwise fail
*/
static int
woal_set_get_scan_cfg(moal_private * priv, struct iwreq *wrq)
{
int data[6], ret = 0;
mlan_ds_scan *scan = NULL;
mlan_ioctl_req *req = NULL;
ENTER();
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_scan));
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
if (wrq->u.data.length > 6) {
ret = -EINVAL;
goto done;
}
scan = (mlan_ds_scan *) req->pbuf;
scan->sub_command = MLAN_OID_SCAN_CONFIG;
req->req_id = MLAN_IOCTL_SCAN;
memset(data, 0, sizeof(data));
if (wrq->u.data.length) {
if (copy_from_user
(data, wrq->u.data.pointer, (wrq->u.data.length * sizeof(int)))) {
PRINTM(MERROR, "Copy from user failed\n");
ret = -EFAULT;
goto done;
}
if ((data[0] < 0) || (data[0] > MLAN_SCAN_TYPE_PASSIVE)) {
PRINTM(MERROR, "Invalid argument for scan type\n");
ret = -EINVAL;
goto done;
}
if ((data[1] < 0) || (data[1] > MLAN_SCAN_MODE_ANY)) {
PRINTM(MERROR, "Invalid argument for scan mode\n");
ret = -EINVAL;
goto done;
}
if ((data[2] < 0) || (data[2] > MAX_PROBES)) {
PRINTM(MERROR, "Invalid argument for scan probes\n");
ret = -EINVAL;
goto done;
}
if (((data[3] < 0) || (data[3] > MRVDRV_MAX_ACTIVE_SCAN_CHAN_TIME)) ||
((data[4] < 0) || (data[4] > MRVDRV_MAX_ACTIVE_SCAN_CHAN_TIME)) ||
((data[5] < 0) || (data[5] > MRVDRV_MAX_PASSIVE_SCAN_CHAN_TIME))) {
PRINTM(MERROR, "Invalid argument for scan time\n");
ret = -EINVAL;
goto done;
}
req->action = MLAN_ACT_SET;
memcpy(&scan->param.scan_cfg, (t_u32 *) data, sizeof(data));
} else
req->action = MLAN_ACT_GET;
if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
ret = -EFAULT;
goto done;
}
if (!wrq->u.data.length) {
memcpy(data, (int *) &scan->param.scan_cfg, sizeof(data));
if (copy_to_user(wrq->u.data.pointer, data, sizeof(data))) {
ret = -EFAULT;
goto done;
}
wrq->u.data.length = sizeof(data) / sizeof(int);
}
done:
if (req)
kfree(req);
LEAVE();
return ret;
}
/** VSIE configuration buffer length */
#define VSIE_MAX_CFG_LEN (MLAN_MAX_VSIE_LEN - 2 + 3)
/** VSIE mask to remove the IE */
#define VSIE_MASK_DISABLE 0x00
/** VSIE Action : Get */
#define VSIE_ACTION_GET 0
/** VSIE Action : Add */
#define VSIE_ACTION_ADD 1
/** VSIE Action : Delete */
#define VSIE_ACTION_DELETE 2
/**
* @brief Get/Add/Remove vendor specific IE
*
* @param priv A pointer to moal_private structure
* @param wrq A pointer to iwreq structure
* @return MLAN_STATUS_SUCCESS --success, otherwise fail
*/
static int
woal_vsie_cfg_ioctl(moal_private * priv, struct iwreq *wrq)
{
int ret = 0, user_data_len = 0, ie_len = 0;
mlan_ds_misc_cfg *misc = NULL;
mlan_ioctl_req *req = NULL;
t_u8 *buf = NULL;
ENTER();
user_data_len = wrq->u.data.length;
if (user_data_len < 2 || user_data_len == 3 ||
user_data_len > VSIE_MAX_CFG_LEN) {
PRINTM(MERROR, "Invalid argument number!\n");
LEAVE();
return -EINVAL;
}
if (!(buf = (t_u8 *) kmalloc(VSIE_MAX_CFG_LEN, GFP_ATOMIC))) {
PRINTM(MERROR, "Cannot allocate buffer for command!\n");
LEAVE();
return -ENOMEM;
}
memset(buf, 0, VSIE_MAX_CFG_LEN);
if (copy_from_user(buf, wrq->u.data.pointer, user_data_len)) {
PRINTM(MINFO, "Copy from user failed\n");
ret = -EFAULT;
goto done;
}
if ((buf[0] > VSIE_ACTION_DELETE) ||
(buf[1] > MLAN_MAX_VSIE_NUM - 1) ||
((buf[0] == VSIE_ACTION_ADD) &&
!(buf[2] &&
buf[2] <=
(MLAN_VSIE_MASK_SCAN | MLAN_VSIE_MASK_ASSOC |
MLAN_VSIE_MASK_ADHOC)))) {
PRINTM(MERROR, "Invalid argument!\n");
ret = -EINVAL;
goto done;
}
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg));
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
req->req_id = MLAN_IOCTL_MISC_CFG;
misc = (mlan_ds_misc_cfg *) req->pbuf;
misc->sub_command = MLAN_OID_MISC_VS_IE;
misc->param.vsie.id = buf[1];
misc->param.vsie.mask = VSIE_MASK_DISABLE;
switch (buf[0]) {
case VSIE_ACTION_GET:
req->action = MLAN_ACT_GET;
break;
case VSIE_ACTION_ADD:
ie_len = user_data_len - 3;
misc->param.vsie.mask = buf[2];
misc->param.vsie.ie[1] = ie_len;
memcpy(&misc->param.vsie.ie[2], &buf[3], ie_len);
case VSIE_ACTION_DELETE:
/* Set with mask 0 is remove */
req->action = MLAN_ACT_SET;
break;
default:
break;
}
if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
ret = -EFAULT;
goto done;
}
wrq->u.data.length = misc->param.vsie.ie[1];
if (wrq->u.data.length) {
if (copy_to_user
(wrq->u.data.pointer, &misc->param.vsie.ie[2],
wrq->u.data.length)) {
PRINTM(MINFO, "Copy to user failed\n");
ret = -EFAULT;
goto done;
}
}
done:
if (buf)
kfree(buf);
if (req)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief Set/Get PS configuration parameters
*
* @param priv A pointer to moal_private structure
* @param wrq A pointer to iwreq structure
*
* @return 0 --success, otherwise fail
*/
static int
woal_set_get_ps_cfg(moal_private * priv, struct iwreq *wrq)
{
int data[7], ret = 0;
mlan_ds_pm_cfg *pm_cfg = NULL;
mlan_ioctl_req *req = NULL;
int allowed = 3;
int i = 3;
ENTER();
allowed++; /* For ad-hoc awake period parameter */
allowed++; /* For beacon missing timeout parameter */
allowed += 2; /* For delay to PS and PS mode parameters */
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_pm_cfg));
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
if (wrq->u.data.length > allowed) {
ret = -EINVAL;
goto done;
}
pm_cfg = (mlan_ds_pm_cfg *) req->pbuf;
pm_cfg->sub_command = MLAN_OID_PM_CFG_PS_CFG;
req->req_id = MLAN_IOCTL_PM_CFG;
memset(data, 0, sizeof(data));
if (wrq->u.data.length) {
if (copy_from_user
(data, wrq->u.data.pointer, (wrq->u.data.length * sizeof(int)))) {
PRINTM(MERROR, "Copy from user failed\n");
ret = -EFAULT;
goto done;
}
if ((data[0] < PS_NULL_DISABLE)) {
PRINTM(MERROR, "Invalid argument for PS null interval\n");
ret = -EINVAL;
goto done;
}
if ((data[1] != IGNORE_MULTIPLE_DTIM) &&
((data[1] < MIN_MULTIPLE_DTIM) || (data[1] > MAX_MULTIPLE_DTIM))) {
PRINTM(MERROR, "Invalid argument for multiple DTIM\n");
ret = -EINVAL;
goto done;
}
if (data[2] < MIN_LISTEN_INTERVAL) {
PRINTM(MERROR, "Invalid argument for listen interval\n");
ret = -EINVAL;
goto done;
}
if ((data[i] != SPECIAL_ADHOC_AWAKE_PD) &&
((data[i] < MIN_ADHOC_AWAKE_PD) ||
(data[i] > MAX_ADHOC_AWAKE_PD))) {
PRINTM(MERROR, "Invalid argument for adhoc awake period\n");
ret = -EINVAL;
goto done;
}
i++;
if ((data[i] != DISABLE_BCN_MISS_TO) &&
((data[i] < MIN_BCN_MISS_TO) || (data[i] > MAX_BCN_MISS_TO))) {
PRINTM(MERROR, "Invalid argument for beacon miss timeout\n");
ret = -EINVAL;
goto done;
}
i++;
if (wrq->u.data.length < allowed - 1)
data[i] = DELAY_TO_PS_UNCHANGED;
else if ((data[i] < MIN_DELAY_TO_PS) || (data[i] > MAX_DELAY_TO_PS)) {
PRINTM(MERROR, "Invalid argument for delay to PS\n");
ret = -EINVAL;
goto done;
}
i++;
if ((data[i] != PS_MODE_UNCHANGED) && (data[i] != PS_MODE_AUTO) &&
(data[i] != PS_MODE_POLL) && (data[i] != PS_MODE_NULL)) {
PRINTM(MERROR, "Invalid argument for PS mode\n");
ret = -EINVAL;
goto done;
}
i++;
req->action = MLAN_ACT_SET;
memcpy(&pm_cfg->param.ps_cfg, (t_u32 *) data,
sizeof(pm_cfg->param.ps_cfg));
} else
req->action = MLAN_ACT_GET;
if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
ret = -EFAULT;
goto done;
}
memcpy(data, (int *) &pm_cfg->param.ps_cfg, sizeof(pm_cfg->param.ps_cfg));
if (copy_to_user(wrq->u.data.pointer, data, sizeof(int) * allowed)) {
ret = -EFAULT;
goto done;
}
wrq->u.data.length = allowed;
done:
if (req)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief Private IOCTL entry to send an ADDTS TSPEC
*
* Receive a ADDTS command from the application. The command structure
* contains a TSPEC and timeout in milliseconds. The timeout is performed
* in the firmware after the ADDTS command frame is sent.
*
* The TSPEC is received in the API as an opaque block whose length is
* calculated from the IOCTL data length. The firmware will send the
* entire data block, including the bytes after the TSPEC. This is done
* to allow extra IEs to be packaged with the TSPEC in the ADDTS action
* frame.
*
* The IOCTL structure contains two return fields:
* - The firmware command result, which indicates failure and timeouts
* - The IEEE Status code which contains the corresponding value from
* any ADDTS response frame received.
*
* In addition, the opaque TSPEC data block passed in is replaced with the
* TSPEC received in the ADDTS response frame. In case of failure, the
* AP may modify the TSPEC on return and in the case of success, the
* medium time is returned as calculated by the AP. Along with the TSPEC,
* any IEs that are sent in the ADDTS response are also returned and can be
* parsed using the IOCTL length as an indicator of extra elements.
*
* The return value to the application layer indicates a driver execution
* success or failure. A successful return could still indicate a firmware
* failure or AP negotiation failure via the commandResult field copied
* back to the application.
*
* @param priv Pointer to the mlan_private driver data struct
* @param wrq A pointer to iwreq structure containing the
* wlan_ioctl_wmm_addts_req_t struct for this ADDTS request
*
* @return 0 if successful; IOCTL error code otherwise
*/
static int
woal_wmm_addts_req_ioctl(moal_private * priv, struct iwreq *wrq)
{
static t_u8 dialog_tok = 0;
mlan_ioctl_req *req = NULL;
mlan_ds_wmm_cfg *cfg = NULL;
wlan_ioctl_wmm_addts_req_t addts_ioctl;
int ret = 0;
ENTER();
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_wmm_cfg));
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
req->req_id = MLAN_IOCTL_WMM_CFG;
cfg = (mlan_ds_wmm_cfg *) req->pbuf;
cfg->sub_command = MLAN_OID_WMM_CFG_ADDTS;
memset(&addts_ioctl, 0, sizeof(addts_ioctl));
if (wrq->u.data.length) {
if (copy_from_user(&addts_ioctl, wrq->u.data.pointer,
MIN(wrq->u.data.length, sizeof(addts_ioctl)))) {
PRINTM(MERROR, "TSPEC: ADDTS copy from user failed\n");
ret = -EFAULT;
goto done;
}
if ((++dialog_tok) == 0)
dialog_tok = 1;
cfg->param.addts.dialog_tok = dialog_tok;
cfg->param.addts.timeout = addts_ioctl.timeout_ms;
cfg->param.addts.tspec_data_len = (wrq->u.data.length
- sizeof(addts_ioctl.timeout_ms)
- sizeof(addts_ioctl.cmd_result)
-
sizeof(addts_ioctl.
ieee_status_code));
memcpy(cfg->param.addts.tspec_data,
addts_ioctl.tspec_data, cfg->param.addts.tspec_data_len);
if (MLAN_STATUS_SUCCESS !=
woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
ret = -EFAULT;
goto done;
}
addts_ioctl.cmd_result = cfg->param.addts.result;
addts_ioctl.ieee_status_code = (t_u8) cfg->param.addts.status_code;
memcpy(addts_ioctl.tspec_data,
cfg->param.addts.tspec_data, cfg->param.addts.tspec_data_len);
wrq->u.data.length = (sizeof(addts_ioctl.timeout_ms)
+ sizeof(addts_ioctl.cmd_result)
+ sizeof(addts_ioctl.ieee_status_code)
+ cfg->param.addts.tspec_data_len);
if (copy_to_user(wrq->u.data.pointer, &addts_ioctl, wrq->u.data.length)) {
PRINTM(MERROR, "TSPEC: ADDTS copy to user failed\n");
ret = -EFAULT;
goto done;
}
}
done:
if (req)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief Private IOCTL entry to send a DELTS TSPEC
*
* Receive a DELTS command from the application. The command structure
* contains a TSPEC and reason code along with space for a command result
* to be returned. The information is packaged is sent to the wlan_cmd.c
* firmware command prep and send routines for execution in the firmware.
*
* The reason code is not used for WMM implementations but is indicated in
* the 802.11e specification.
*
* The return value to the application layer indicates a driver execution
* success or failure. A successful return could still indicate a firmware
* failure via the cmd_result field copied back to the application.
*
* @param priv Pointer to the mlan_private driver data struct
* @param wrq A pointer to iwreq structure containing the
* wlan_ioctl_wmm_delts_req_t struct for this DELTS request
*
* @return 0 if successful; IOCTL error code otherwise
*/
static int
woal_wmm_delts_req_ioctl(moal_private * priv, struct iwreq *wrq)
{
mlan_ioctl_req *req = NULL;
mlan_ds_wmm_cfg *cfg = NULL;
wlan_ioctl_wmm_delts_req_t delts_ioctl;
int ret = 0;
ENTER();
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_wmm_cfg));
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
req->req_id = MLAN_IOCTL_WMM_CFG;
cfg = (mlan_ds_wmm_cfg *) req->pbuf;
cfg->sub_command = MLAN_OID_WMM_CFG_DELTS;
memset(&delts_ioctl, 0, sizeof(delts_ioctl));
if (wrq->u.data.length) {
if (copy_from_user(&delts_ioctl, wrq->u.data.pointer,
MIN(wrq->u.data.length, sizeof(delts_ioctl)))) {
PRINTM(MERROR, "TSPEC: DELTS copy from user failed\n");
ret = -EFAULT;
goto done;
}
cfg->param.delts.status_code = (t_u32) delts_ioctl.ieee_reason_code;
/* Calculate the length of the TSPEC */
cfg->param.delts.tspec_data_len = (wrq->u.data.length
- sizeof(delts_ioctl.cmd_result)
-
sizeof(delts_ioctl.
ieee_reason_code));
memcpy(cfg->param.delts.tspec_data,
delts_ioctl.tspec_data, cfg->param.delts.tspec_data_len);
if (MLAN_STATUS_SUCCESS !=
woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
ret = -EFAULT;
goto done;
}
/* Return the firmware command result back to the application layer */
delts_ioctl.cmd_result = cfg->param.delts.result;
wrq->u.data.length = sizeof(delts_ioctl);
if (copy_to_user(wrq->u.data.pointer, &delts_ioctl, wrq->u.data.length)) {
PRINTM(MERROR, "TSPEC: DELTS copy to user failed\n");
ret = -EFAULT;
goto done;
}
}
done:
if (req)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief Private IOCTL entry to get/set a specified AC Queue's parameters
*
* Receive a AC Queue configuration command which is used to get, set, or
* default the parameters associated with a specific WMM AC Queue.
*
* @param priv Pointer to the mlan_private driver data struct
* @param wrq A pointer to iwreq structure containing the
* wlan_ioctl_wmm_queue_config_t struct
*
* @return 0 if successful; IOCTL error code otherwise
*/
static int
woal_wmm_queue_config_ioctl(moal_private * priv, struct iwreq *wrq)
{
mlan_ioctl_req *req = NULL;
mlan_ds_wmm_cfg *pwmm = NULL;
mlan_ds_wmm_queue_config *pqcfg = NULL;
wlan_ioctl_wmm_queue_config_t qcfg_ioctl;
int ret = 0;
ENTER();
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_wmm_cfg));
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
req->req_id = MLAN_IOCTL_WMM_CFG;
pwmm = (mlan_ds_wmm_cfg *) req->pbuf;
pwmm->sub_command = MLAN_OID_WMM_CFG_QUEUE_CONFIG;
memset(&qcfg_ioctl, 0, sizeof(qcfg_ioctl));
pqcfg = (mlan_ds_wmm_queue_config *) & pwmm->param.q_cfg;
if (wrq->u.data.length) {
if (copy_from_user(&qcfg_ioctl, wrq->u.data.pointer,
MIN(wrq->u.data.length, sizeof(qcfg_ioctl)))) {
PRINTM(MERROR, "QCONFIG: copy from user failed\n");
ret = -EFAULT;
goto done;
}
pqcfg->action = qcfg_ioctl.action;
pqcfg->access_category = qcfg_ioctl.access_category;
pqcfg->msdu_lifetime_expiry = qcfg_ioctl.msdu_lifetime_expiry;
if (MLAN_STATUS_SUCCESS !=
woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
ret = -EFAULT;
goto done;
}
memset(&qcfg_ioctl, 0, sizeof(qcfg_ioctl));
qcfg_ioctl.action = pqcfg->action;
qcfg_ioctl.access_category = pqcfg->access_category;
qcfg_ioctl.msdu_lifetime_expiry = pqcfg->msdu_lifetime_expiry;
wrq->u.data.length = sizeof(qcfg_ioctl);
if (copy_to_user(wrq->u.data.pointer, &qcfg_ioctl, wrq->u.data.length)) {
PRINTM(MERROR, "QCONFIG: copy to user failed\n");
ret = -EFAULT;
goto done;
}
}
done:
if (req)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief Private IOCTL entry to get and start/stop queue stats on a WMM AC
*
* Receive a AC Queue statistics command from the application for a specific
* WMM AC. The command can:
* - Turn stats on
* - Turn stats off
* - Collect and clear the stats
*
* @param priv Pointer to the moal_private driver data struct
* @param wrq A pointer to iwreq structure containing the
* wlan_ioctl_wmm_queue_stats_t struct
*
* @return 0 if successful; IOCTL error code otherwise
*/
static int
woal_wmm_queue_stats_ioctl(moal_private * priv, struct iwreq *wrq)
{
mlan_ioctl_req *req = NULL;
mlan_ds_wmm_cfg *pwmm = NULL;
mlan_ds_wmm_queue_stats *pqstats = NULL;
wlan_ioctl_wmm_queue_stats_t qstats_ioctl;
int ret = 0;
ENTER();
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_wmm_cfg));
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
req->req_id = MLAN_IOCTL_WMM_CFG;
pwmm = (mlan_ds_wmm_cfg *) req->pbuf;
pwmm->sub_command = MLAN_OID_WMM_CFG_QUEUE_STATS;
memset(&qstats_ioctl, 0, sizeof(qstats_ioctl));
pqstats = (mlan_ds_wmm_queue_stats *) & pwmm->param.q_stats;
if (wrq->u.data.length) {
if (copy_from_user(&qstats_ioctl, wrq->u.data.pointer,
MIN(wrq->u.data.length, sizeof(qstats_ioctl)))) {
PRINTM(MERROR, "QSTATS: copy from user failed\n");
ret = -EFAULT;
goto done;
}
memcpy((void *) pqstats, (void *) &qstats_ioctl, sizeof(qstats_ioctl));
PRINTM(MINFO, "QSTATS: IOCTL [%d,%d]\n", qstats_ioctl.action,
qstats_ioctl.access_category);
if (MLAN_STATUS_SUCCESS !=
woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
ret = -EFAULT;
goto done;
}
memset(&qstats_ioctl, 0, sizeof(qstats_ioctl));
memcpy((void *) &qstats_ioctl, (void *) pqstats, sizeof(qstats_ioctl));
wrq->u.data.length = sizeof(qstats_ioctl);
if (copy_to_user
(wrq->u.data.pointer, &qstats_ioctl, wrq->u.data.length)) {
PRINTM(MERROR, "QSTATS: copy to user failed\n");
ret = -EFAULT;
goto done;
}
}
done:
if (req)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief Private IOCTL entry to get the status of the WMM queues
*
* Return the following information for each WMM AC:
* - WMM IE Acm Required
* - Firmware Flow Required
* - Firmware Flow Established
* - Firmware Queue Enabled
* - Firmware Delivery Enabled
* - Firmware Trigger Enabled
*
* @param priv Pointer to the moal_private driver data struct
* @param wrq A pointer to iwreq structure containing the
* wlan_ioctl_wmm_queue_status_t struct for request
*
* @return 0 if successful; IOCTL error code otherwise
*/
static int
woal_wmm_queue_status_ioctl(moal_private * priv, struct iwreq *wrq)
{
mlan_ioctl_req *req = NULL;
mlan_ds_wmm_cfg *pwmm = NULL;
wlan_ioctl_wmm_queue_status_t qstatus_ioctl;
int ret = 0;
ENTER();
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_wmm_cfg));
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
req->req_id = MLAN_IOCTL_WMM_CFG;
pwmm = (mlan_ds_wmm_cfg *) req->pbuf;
pwmm->sub_command = MLAN_OID_WMM_CFG_QUEUE_STATUS;
if (wrq->u.data.length == sizeof(qstatus_ioctl)) {
if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_NO_WAIT)) {
ret = -EFAULT;
goto done;
}
memset(&qstatus_ioctl, 0, sizeof(qstatus_ioctl));
memcpy((void *) &qstatus_ioctl, (void *) &pwmm->param.q_status,
sizeof(qstatus_ioctl));
wrq->u.data.length = sizeof(qstatus_ioctl);
} else {
wrq->u.data.length = 0;
}
if (copy_to_user(wrq->u.data.pointer, &qstatus_ioctl, wrq->u.data.length)) {
PRINTM(MERROR, "QSTATUS: copy to user failed\n");
ret = -EFAULT;
goto done;
}
done:
if (req)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief Private IOCTL entry to get the status of the WMM Traffic Streams
*
* @param priv Pointer to the moal_private driver data struct
* @param wrq A pointer to iwreq structure containing the
* wlan_ioctl_wmm_ts_status_t struct for request
*
* @return 0 if successful; IOCTL error code otherwise
*/
static int
woal_wmm_ts_status_ioctl(moal_private * priv, struct iwreq *wrq)
{
mlan_ioctl_req *req = NULL;
mlan_ds_wmm_cfg *pwmm = NULL;
wlan_ioctl_wmm_ts_status_t ts_status_ioctl;
int ret = 0;
ENTER();
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_wmm_cfg));
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
req->req_id = MLAN_IOCTL_WMM_CFG;
pwmm = (mlan_ds_wmm_cfg *) req->pbuf;
pwmm->sub_command = MLAN_OID_WMM_CFG_TS_STATUS;
memset(&ts_status_ioctl, 0, sizeof(ts_status_ioctl));
if (wrq->u.data.length == sizeof(ts_status_ioctl)) {
if (copy_from_user(&ts_status_ioctl, wrq->u.data.pointer,
MIN(wrq->u.data.length, sizeof(ts_status_ioctl)))) {
PRINTM(MERROR, "TSTATUS: copy from user failed\n");
ret = -EFAULT;
goto done;
}
memset(&pwmm->param.ts_status, 0, sizeof(ts_status_ioctl));
pwmm->param.ts_status.tid = ts_status_ioctl.tid;
if (MLAN_STATUS_SUCCESS !=
woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
ret = -EFAULT;
goto done;
}
memset(&ts_status_ioctl, 0, sizeof(ts_status_ioctl));
memcpy((void *) &ts_status_ioctl, (void *) &pwmm->param.ts_status,
sizeof(ts_status_ioctl));
wrq->u.data.length = sizeof(ts_status_ioctl);
} else {
wrq->u.data.length = 0;
}
if (copy_to_user(wrq->u.data.pointer, &ts_status_ioctl, wrq->u.data.length)) {
PRINTM(MERROR, "TSTATUS: copy to user failed\n");
ret = -EFAULT;
goto done;
}
done:
if (req)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief Set/Get auth type
*
* @param priv Pointer to the moal_private driver data struct
* @param wrq A pointer to iwreq structure
*
* @return 0 --success, otherwise fail
*/
static int
woal_auth_type(moal_private * priv, struct iwreq *wrq)
{
int auth_type;
t_u32 auth_mode;
int ret = 0;
ENTER();
if (wrq->u.data.length == 0) {
if (MLAN_STATUS_SUCCESS !=
woal_get_auth_mode(priv, MOAL_IOCTL_WAIT, &auth_mode)) {
ret = -EFAULT;
goto done;
}
auth_type = auth_mode;
if (copy_to_user(wrq->u.data.pointer, &auth_type, sizeof(auth_type))) {
PRINTM(MERROR, "Copy to user failed\n");
ret = -EFAULT;
goto done;
}
wrq->u.data.length = 1;
} else {
if (copy_from_user(&auth_type, wrq->u.data.pointer, sizeof(auth_type))) {
PRINTM(MERROR, "Copy from user failed\n");
ret = -EFAULT;
goto done;
}
PRINTM(MINFO, "SET: auth_type %d\n", auth_type);
if (((auth_type < MLAN_AUTH_MODE_OPEN) ||
(auth_type > MLAN_AUTH_MODE_SHARED))
&& (auth_type != MLAN_AUTH_MODE_AUTO)) {
ret = -EINVAL;
goto done;
}
auth_mode = auth_type;
if (MLAN_STATUS_SUCCESS !=
woal_set_auth_mode(priv, MOAL_IOCTL_WAIT, auth_mode)) {
ret = -EFAULT;
goto done;
}
}
done:
LEAVE();
return ret;
}
/********************************************************
Global Functions
********************************************************/
/**
* @brief Get version
*
* @param handle A pointer to moal_handle structure
* @param version A pointer to version buffer
* @param max_len max length of version buffer
*
* @return N/A
*/
void
woal_get_version(moal_handle * handle, char *version, int max_len)
{
union
{
t_u32 l;
t_u8 c[4];
} ver;
char fw_ver[32];
ENTER();
ver.l = handle->fw_release_number;
sprintf(fw_ver, "%u.%u.%u.p%u", ver.c[2], ver.c[1], ver.c[0], ver.c[3]);
snprintf(version, max_len, driver_version, fw_ver);
LEAVE();
}
/**
* @brief ioctl function - entry point
*
* @param dev A pointer to net_device structure
* @param req A pointer to ifreq structure
* @param cmd Command
*
* @return 0 --success, otherwise fail
*/
int
woal_do_ioctl(struct net_device *dev, struct ifreq *req, int cmd)
{
moal_private *priv = (moal_private *) netdev_priv(dev);
struct iwreq *wrq = (struct iwreq *) req;
int ret = 0;
ENTER();
PRINTM(MINFO, "woal_do_ioctl: ioctl cmd = 0x%x\n", cmd);
switch (cmd) {
case WOAL_SETONEINT_GETWORDCHAR:
switch (wrq->u.data.flags) {
case WOAL_VERSION: /* Get driver version */
ret = woal_get_driver_version(priv, req);
break;
case WOAL_VEREXT: /* Get extended driver version */
ret = woal_get_driver_verext(priv, req);
break;
default:
ret = -EOPNOTSUPP;
break;
}
break;
case WOAL_SETNONE_GETNONE:
switch (wrq->u.data.flags) {
case WOAL_WARMRESET:
ret = woal_warm_reset(priv);
break;
default:
ret = -EOPNOTSUPP;
break;
}
break;
case WOAL_SETONEINT_GETONEINT:
switch (wrq->u.data.flags) {
case WOAL_SET_GET_TXRATE:
ret = woal_set_get_txrate(priv, wrq);
break;
case WOAL_SET_GET_REGIONCODE:
ret = woal_set_get_regioncode(priv, wrq);
break;
case WOAL_SET_RADIO:
ret = woal_set_get_radio(priv, wrq);
break;
case WOAL_WMM_ENABLE:
ret = woal_wmm_enable_ioctl(priv, wrq);
break;
case WOAL_11D_ENABLE:
ret = woal_11d_enable_ioctl(priv, wrq);
break;
case WOAL_SET_GET_TX_RX_ANT:
ret = woal_set_get_tx_rx_ant(priv, wrq);
break;
case WOAL_SET_GET_QOS_CFG:
ret = woal_set_get_qos_cfg(priv, wrq);
break;
case WOAL_SET_GET_LDO_CFG:
ret = woal_set_get_ldo_cfg(priv, wrq);
break;
#ifdef REASSOCIATION
case WOAL_SET_GET_REASSOC:
ret = woal_set_get_reassoc(priv, wrq);
break;
#endif /* REASSOCIATION */
case WOAL_TXBUF_CFG:
ret = woal_txbuf_cfg(priv, wrq);
break;
case WOAL_SET_GET_WWS_CFG:
ret = woal_wws_cfg(priv, wrq);
break;
case WOAL_SLEEP_PD:
ret = woal_sleep_pd(priv, wrq);
break;
case WOAL_AUTH_TYPE:
ret = woal_auth_type(priv, wrq);
break;
default:
ret = -EOPNOTSUPP;
break;
}
break;
case WOAL_SET_GET_SIXTEEN_INT:
switch ((int) wrq->u.data.flags) {
case WOAL_TX_POWERCFG:
ret = woal_tx_power_cfg(priv, wrq);
break;
#ifdef DEBUG_LEVEL1
case WOAL_DRV_DBG:
ret = woal_drv_dbg(priv, wrq);
break;
#endif
case WOAL_BEACON_INTERVAL:
ret = woal_beacon_interval(priv, wrq);
break;
case WOAL_ATIM_WINDOW:
ret = woal_atim_window(priv, wrq);
break;
case WOAL_SIGNAL:
ret = woal_get_signal(priv, wrq);
break;
case WOAL_DEEP_SLEEP:
ret = woal_deep_sleep_ioctl(priv, wrq);
break;
case WOAL_11N_TX_CFG:
ret = woal_11n_tx_cfg(priv, wrq);
break;
case WOAL_11N_AMSDU_AGGR_CTRL:
ret = woal_11n_amsdu_aggr_ctrl(priv, wrq);
break;
case WOAL_11N_HTCAP_CFG:
ret = woal_11n_htcap_cfg(priv, wrq);
break;
case WOAL_PRIO_TBL:
ret = woal_11n_prio_tbl(priv, wrq);
break;
case WOAL_ADDBA_UPDT:
ret = woal_addba_para_updt(priv, wrq);
break;
case WOAL_ADDBA_REJECT:
ret = woal_addba_reject(priv, wrq);
break;
case WOAL_HS_CFG:
ret = woal_hs_cfg(priv, wrq, MTRUE);
break;
case WOAL_HS_SETPARA:
ret = woal_hs_setpara(priv, wrq);
break;
case WOAL_REG_READ_WRITE:
ret = woal_reg_read_write(priv, wrq);
break;
case WOAL_INACTIVITY_TIMEOUT_EXT:
ret = woal_inactivity_timeout_ext(priv, wrq);
break;
case WOAL_SDIO_CLOCK:
ret = woal_sdio_clock_ioctl(priv, wrq);
break;
case WOAL_BAND_CFG:
ret = woal_band_cfg(priv, wrq);
break;
case WOAL_BCA_TIME_SHARE:
ret = woal_bca_time_share(priv, wrq);
break;
case WOAL_TID_ELIG_TBL:
ret = woal_set_get_tid_elig_tbl(priv, wrq);
break;
case WOAL_SCAN_CFG:
ret = woal_set_get_scan_cfg(priv, wrq);
break;
case WOAL_PS_CFG:
ret = woal_set_get_ps_cfg(priv, wrq);
break;
case WOAL_MEM_READ_WRITE:
ret = woal_mem_read_write(priv, wrq);
break;
#if defined(SDIO_MULTI_PORT_TX_AGGR) || defined(SDIO_MULTI_PORT_RX_AGGR)
case WOAL_SDIO_MPA_CTRL:
ret = woal_do_sdio_mpa_ctrl(priv, wrq);
break;
#endif
case WOAL_SLEEP_PARAMS:
ret = woal_sleep_params_ioctl(priv, wrq);
break;
default:
ret = -EINVAL;
break;
}
break;
case WOALGETLOG:
ret = woal_get_log(priv, wrq);
break;
case WOAL_SET_GET_256_CHAR:
switch (wrq->u.data.flags) {
case WOAL_PASSPHRASE:
ret = woal_passphrase(priv, wrq);
break;
case WOAL_ADHOC_AES:
ret = woal_adhoc_aes_ioctl(priv, wrq);
break;
case WOAL_WMM_QUEUE_STATUS:
ret = woal_wmm_queue_status_ioctl(priv, wrq);
break;
case WOAL_WMM_TS_STATUS:
ret = woal_wmm_ts_status_ioctl(priv, wrq);
break;
default:
ret = -EINVAL;
break;
}
break;
case WOAL_SETADDR_GETNONE:
switch ((int) wrq->u.data.flags) {
case WOAL_DEAUTH:
ret = woal_deauth(priv, wrq);
break;
default:
ret = -EINVAL;
break;
}
break;
case WOAL_SETNONE_GETTWELVE_CHAR:
/*
* We've not used IW_PRIV_TYPE_FIXED so sub-ioctl number is
* in flags of iwreq structure, otherwise it will be in
* mode member of iwreq structure.
*/
switch ((int) wrq->u.data.flags) {
case WOAL_WPS_SESSION:
ret = woal_wps_cfg_ioctl(priv, wrq);
break;
default:
ret = -EINVAL;
break;
}
break;
case WOAL_SETNONE_GET_FOUR_INT:
switch ((int) wrq->u.data.flags) {
case WOAL_DATA_RATE:
ret = woal_get_txrx_rate(priv, wrq);
break;
case WOAL_ESUPP_MODE:
ret = woal_get_esupp_mode(priv, wrq);
break;
default:
ret = -EINVAL;
break;
}
break;
#ifdef MFG_CMD_SUPPORT
case WOAL_MFG_CMD:
PRINTM(MINFO, "Entering the Manufacturing ioctl SIOCCFMFG\n");
ret = woal_mfg_command(priv, wrq);
PRINTM(MINFO, "Manufacturing Ioctl %s\n", (ret) ? "failed" : "success");
break;
#endif
case WOAL_SET_GET_64_INT:
switch ((int) wrq->u.data.flags) {
case WOAL_ECL_SYS_CLOCK:
ret = woal_ecl_sys_clock(priv, wrq);
break;
}
break;
case WOAL_HOST_CMD:
ret = woal_host_command(priv, wrq);
break;
case WOAL_ARP_FILTER:
ret = woal_arp_filter(priv, wrq);
break;
case WOAL_SET_INTS_GET_CHARS:
switch ((int) wrq->u.data.flags) {
case WOAL_READ_EEPROM:
ret = woal_read_eeprom(priv, wrq);
break;
}
break;
case WOAL_SET_GET_2K_BYTES:
switch ((int) wrq->u.data.flags) {
case WOAL_CMD_52RDWR:
ret = woal_cmd52rdwr_ioctl(priv, wrq);
break;
case WOAL_CMD_53RDWR:
ret = woal_cmd53rdwr_ioctl(priv, wrq);
break;
case WOAL_SET_USER_SCAN:
ret = woal_set_user_scan_ioctl(priv, wrq);
break;
case WOAL_GET_SCAN_TABLE:
ret = woal_get_scan_table_ioctl(priv, wrq);
break;
case WOAL_VSIE_CFG:
ret = woal_vsie_cfg_ioctl(priv, wrq);
break;
case WOAL_WMM_ADDTS:
ret = woal_wmm_addts_req_ioctl(priv, wrq);
break;
case WOAL_WMM_DELTS:
ret = woal_wmm_delts_req_ioctl(priv, wrq);
break;
case WOAL_WMM_QUEUE_CONFIG:
ret = woal_wmm_queue_config_ioctl(priv, wrq);
break;
case WOAL_WMM_QUEUE_STATS:
ret = woal_wmm_queue_stats_ioctl(priv, wrq);
break;
default:
ret = -EINVAL;
break;
}
break;
case WOAL_GET_BSS_TYPE:
ret = woal_get_bss_type(dev, req);
break;
default:
ret = -EINVAL;
break;
}
LEAVE();
return ret;
}
/**
* @brief Send get FW info request to MLAN
*
* @param priv A pointer to moal_private structure
*
* @return None
*/
void
woal_request_get_fw_info(moal_private * priv)
{
mlan_ioctl_req *req = NULL;
mlan_ds_get_info *info;
mlan_status status;
ENTER();
memset(priv->current_addr, 0xff, ETH_ALEN);
/* 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 */
info = (mlan_ds_get_info *) req->pbuf;
req->req_id = MLAN_IOCTL_GET_INFO;
req->action = MLAN_ACT_GET;
info->sub_command = MLAN_OID_GET_FW_INFO;
/* Send IOCTL request to MLAN */
status = woal_request_ioctl(priv, req, MOAL_CMD_WAIT);
if (status == MLAN_STATUS_SUCCESS) {
priv->phandle->fw_release_number = info->param.fw_info.fw_ver;
if (priv->current_addr[0] == 0xff)
memcpy(priv->current_addr, &info->param.fw_info.mac_addr,
sizeof(mlan_802_11_mac_addr));
memcpy(priv->netdev->dev_addr, priv->current_addr, ETH_ALEN);
DBG_HEXDUMP(MIF_D, "mac", priv->current_addr, 6);
} else
PRINTM(MERROR, "get fw info failed! status=%d, error_code=0x%lx\n",
status, req->status_code);
done:
if (req)
kfree(req);
LEAVE();
return;
}
/**
* @brief Get mode
*
* @param priv A pointer to moal_private structure
* @param wait_option Wait option (MOAL_WAIT or MOAL_NO_WAIT)
*
* @return Wireless mode
*/
t_u32
woal_get_mode(moal_private * priv, t_u8 wait_option)
{
int ret = 0;
mlan_ds_bss *bss = NULL;
mlan_ioctl_req *req = NULL;
mlan_status status = MLAN_STATUS_SUCCESS;
t_u32 mode = priv->w_stats.status;
ENTER();
/* Allocate an IOCTL request buffer */
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss));
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
/* Fill request buffer */
bss = (mlan_ds_bss *) req->pbuf;
bss->sub_command = MLAN_OID_BSS_MODE;
req->req_id = MLAN_IOCTL_BSS;
req->action = MLAN_ACT_GET;
/* Send IOCTL request to MLAN */
status = woal_request_ioctl(priv, req, wait_option);
if (status == MLAN_STATUS_SUCCESS) {
switch (bss->param.bss_mode) {
case MLAN_BSS_MODE_INFRA:
mode = IW_MODE_INFRA;
break;
case MLAN_BSS_MODE_IBSS:
mode = IW_MODE_ADHOC;
break;
default:
mode = IW_MODE_AUTO;
break;
}
}
done:
if (req && (status != MLAN_STATUS_PENDING))
kfree(req);
LEAVE();
return mode;
}
/**
* @brief Get RSSI info
*
* @param priv A pointer to moal_private structure
* @param wait_option Wait option
* @param signal A pointer tp mlan_ds_get_signal structure
*
* @return MLAN_STATUS_SUCCESS -- success, otherwise fail
*/
mlan_status
woal_get_signal_info(moal_private * priv, t_u8 wait_option,
mlan_ds_get_signal * signal)
{
int ret = 0;
mlan_ds_get_info *info = NULL;
mlan_ioctl_req *req = NULL;
mlan_status status = MLAN_STATUS_SUCCESS;
ENTER();
/* Allocate an IOCTL request buffer */
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_get_info));
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
/* Fill request buffer */
info = (mlan_ds_get_info *) req->pbuf;
info->sub_command = MLAN_OID_GET_SIGNAL;
info->param.signal.selector = ALL_RSSI_INFO_MASK;
req->req_id = MLAN_IOCTL_GET_INFO;
req->action = MLAN_ACT_GET;
/* Send IOCTL request to MLAN */
status = woal_request_ioctl(priv, req, wait_option);
if (status == MLAN_STATUS_SUCCESS) {
if (signal)
memcpy(signal, &info->param.signal, sizeof(mlan_ds_get_signal));
if (info->param.signal.selector & BCN_RSSI_AVG_MASK)
priv->w_stats.qual.level = info->param.signal.bcn_rssi_avg;
if (info->param.signal.selector & BCN_NF_AVG_MASK)
priv->w_stats.qual.noise = info->param.signal.bcn_nf_avg;
}
done:
if (req && (status != MLAN_STATUS_PENDING))
kfree(req);
LEAVE();
return status;
}
/**
* @brief Get statistics information
*
* @param priv A pointer to moal_private structure
* @param wait_option Wait option
* @param stats A pointer to mlan_ds_get_stats structure
*
* @return MLAN_STATUS_SUCCESS -- success, otherwise fail
*/
mlan_status
woal_get_stats_info(moal_private * priv, t_u8 wait_option,
mlan_ds_get_stats * stats)
{
int ret = 0;
mlan_ds_get_info *info = NULL;
mlan_ioctl_req *req = NULL;
mlan_status status = MLAN_STATUS_SUCCESS;
ENTER();
/* Allocate an IOCTL request buffer */
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_get_info));
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
/* Fill request buffer */
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;
/* Send IOCTL request to MLAN */
status = woal_request_ioctl(priv, req, wait_option);
if (status == MLAN_STATUS_SUCCESS) {
if (stats)
memcpy(stats, &info->param.stats, sizeof(mlan_ds_get_stats));
priv->w_stats.discard.fragment = info->param.stats.fcs_error;
priv->w_stats.discard.retries = info->param.stats.retry;
priv->w_stats.discard.misc = info->param.stats.ack_failure;
}
done:
if (req && (status != MLAN_STATUS_PENDING))
kfree(req);
LEAVE();
return status;
}
/**
* @brief Get data rates
*
* @param priv A pointer to moal_private structure
* @param wait_option Wait option
* @param m_rates A pointer to moal_802_11_rates structure
*
* @return MLAN_STATUS_SUCCESS -- success, otherwise fail
*/
mlan_status
woal_get_data_rates(moal_private * priv, t_u8 wait_option,
moal_802_11_rates * m_rates)
{
int ret = 0;
mlan_ds_rate *rate = NULL;
mlan_ioctl_req *req = NULL;
mlan_status status = MLAN_STATUS_SUCCESS;
ENTER();
/* Allocate an IOCTL request buffer */
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_rate));
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
/* Fill request buffer */
rate = (mlan_ds_rate *) req->pbuf;
rate->sub_command = MLAN_OID_SUPPORTED_RATES;
req->req_id = MLAN_IOCTL_RATE;
req->action = MLAN_ACT_GET;
/* Send IOCTL request to MLAN */
status = woal_request_ioctl(priv, req, wait_option);
if (status == MLAN_STATUS_SUCCESS) {
if (m_rates)
m_rates->num_of_rates =
woal_copy_rates(m_rates->rates, m_rates->num_of_rates,
rate->param.rates, MLAN_SUPPORTED_RATES);
}
done:
if (req && (status != MLAN_STATUS_PENDING))
kfree(req);
LEAVE();
return status;
}
/**
* @brief Get channel list
*
* @param priv A pointer to moal_private structure
* @param wait_option Wait option
* @param chan_list A pointer to mlan_chan_list structure
*
* @return MLAN_STATUS_SUCCESS -- success, otherwise fail
*/
mlan_status
woal_get_channel_list(moal_private * priv, t_u8 wait_option,
mlan_chan_list * chan_list)
{
int ret = 0;
mlan_ds_bss *bss = NULL;
mlan_ioctl_req *req = NULL;
mlan_status status = MLAN_STATUS_SUCCESS;
ENTER();
/* Allocate an IOCTL request buffer */
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss));
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
/* Fill request buffer */
bss = (mlan_ds_bss *) req->pbuf;
bss->sub_command = MLAN_OID_BSS_CHANNEL_LIST;
req->req_id = MLAN_IOCTL_BSS;
req->action = MLAN_ACT_GET;
/* Send IOCTL request to MLAN */
status = woal_request_ioctl(priv, req, wait_option);
if (status == MLAN_STATUS_SUCCESS) {
if (chan_list) {
memcpy(chan_list, &bss->param.chanlist, sizeof(mlan_chan_list));
}
}
done:
if (req && (status != MLAN_STATUS_PENDING))
kfree(req);
LEAVE();
return status;
}
/**
* @brief Get scan table
*
* @param priv A pointer to moal_private structure
* @param wait_option Wait option
* @param scan_resp A pointer to mlan_scan_resp structure
*
* @return MLAN_STATUS_SUCCESS -- success, otherwise fail
*/
mlan_status
woal_get_scan_table(moal_private * priv, t_u8 wait_option,
mlan_scan_resp * scan_resp)
{
int ret = 0;
mlan_ioctl_req *req = NULL;
mlan_ds_scan *scan = NULL;
mlan_status status = MLAN_STATUS_SUCCESS;
ENTER();
/* Allocate an IOCTL request buffer */
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_scan));
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
/* Fill request buffer */
scan = (mlan_ds_scan *) req->pbuf;
scan->sub_command = MLAN_OID_SCAN_NORMAL;
req->req_id = MLAN_IOCTL_SCAN;
req->action = MLAN_ACT_GET;
memcpy((void *) &scan->param.scan_resp, (void *) scan_resp,
sizeof(mlan_scan_resp));
/* Send IOCTL request to MLAN */
status = woal_request_ioctl(priv, req, wait_option);
if (status == MLAN_STATUS_SUCCESS) {
if (scan_resp) {
memcpy(scan_resp, &scan->param.scan_resp, sizeof(mlan_scan_resp));
}
}
done:
if (req && (status != MLAN_STATUS_PENDING))
kfree(req);
LEAVE();
return status;
}
/**
* @brief Set authentication mode
*
* @param priv A pointer to moal_private structure
* @param wait_option Wait option
* @param auth_mode Authentication mode
*
* @return MLAN_STATUS_SUCCESS -- success, otherwise fail
*/
mlan_status
woal_set_auth_mode(moal_private * priv, t_u8 wait_option, t_u32 auth_mode)
{
int ret = 0;
mlan_ioctl_req *req = NULL;
mlan_ds_sec_cfg *sec = NULL;
mlan_status status = MLAN_STATUS_SUCCESS;
ENTER();
/* Allocate an IOCTL request buffer */
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg));
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
/* Fill request buffer */
sec = (mlan_ds_sec_cfg *) req->pbuf;
sec->sub_command = MLAN_OID_SEC_CFG_AUTH_MODE;
req->req_id = MLAN_IOCTL_SEC_CFG;
req->action = MLAN_ACT_SET;
sec->param.auth_mode = auth_mode;
/* Send IOCTL request to MLAN */
status = woal_request_ioctl(priv, req, wait_option);
done:
if (req && (status != MLAN_STATUS_PENDING))
kfree(req);
LEAVE();
return status;
}
/**
* @brief Get authentication mode
*
* @param priv A pointer to moal_private structure
* @param wait_option Wait option
* @param auth_mode A pointer to authentication mode
*
* @return MLAN_STATUS_SUCCESS -- success, otherwise fail
*/
mlan_status
woal_get_auth_mode(moal_private * priv, t_u8 wait_option, t_u32 * auth_mode)
{
int ret = 0;
mlan_ioctl_req *req = NULL;
mlan_ds_sec_cfg *sec = NULL;
mlan_status status = MLAN_STATUS_SUCCESS;
ENTER();
/* Allocate an IOCTL request buffer */
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg));
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
/* Fill request buffer */
sec = (mlan_ds_sec_cfg *) req->pbuf;
sec->sub_command = MLAN_OID_SEC_CFG_AUTH_MODE;
req->req_id = MLAN_IOCTL_SEC_CFG;
req->action = MLAN_ACT_GET;
/* Send IOCTL request to MLAN */
status = woal_request_ioctl(priv, req, wait_option);
if (status == MLAN_STATUS_SUCCESS && auth_mode) {
*auth_mode = sec->param.auth_mode;
}
done:
if (req && (status != MLAN_STATUS_PENDING))
kfree(req);
LEAVE();
return status;
}
/**
* @brief Set encrypt mode
*
* @param priv A pointer to moal_private structure
* @param wait_option Wait option
* @param encrypt_mode Encryption mode
*
* @return MLAN_STATUS_SUCCESS -- success, otherwise fail
*/
mlan_status
woal_set_encrypt_mode(moal_private * priv, t_u8 wait_option, t_u32 encrypt_mode)
{
int ret = 0;
mlan_ioctl_req *req = NULL;
mlan_ds_sec_cfg *sec = NULL;
mlan_status status = MLAN_STATUS_SUCCESS;
ENTER();
/* Allocate an IOCTL request buffer */
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg));
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
/* Fill request buffer */
sec = (mlan_ds_sec_cfg *) req->pbuf;
sec->sub_command = MLAN_OID_SEC_CFG_ENCRYPT_MODE;
req->req_id = MLAN_IOCTL_SEC_CFG;
req->action = MLAN_ACT_SET;
sec->param.encrypt_mode = encrypt_mode;
/* Send IOCTL request to MLAN */
status = woal_request_ioctl(priv, req, wait_option);
done:
if (req && (status != MLAN_STATUS_PENDING))
kfree(req);
LEAVE();
return status;
}
/**
* @brief Get encrypt mode
*
* @param priv A pointer to moal_private structure
* @param wait_option Wait option
* @param encrypt_mode A pointer to encrypt mode
*
* @return MLAN_STATUS_SUCCESS -- success, otherwise fail
*/
mlan_status
woal_get_encrypt_mode(moal_private * priv, t_u8 wait_option,
t_u32 * encrypt_mode)
{
int ret = 0;
mlan_ioctl_req *req = NULL;
mlan_ds_sec_cfg *sec = NULL;
mlan_status status = MLAN_STATUS_SUCCESS;
ENTER();
/* Allocate an IOCTL request buffer */
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg));
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
/* Fill request buffer */
sec = (mlan_ds_sec_cfg *) req->pbuf;
sec->sub_command = MLAN_OID_SEC_CFG_ENCRYPT_MODE;
req->req_id = MLAN_IOCTL_SEC_CFG;
req->action = MLAN_ACT_GET;
/* Send IOCTL request to MLAN */
status = woal_request_ioctl(priv, req, wait_option);
if (status == MLAN_STATUS_SUCCESS && encrypt_mode) {
*encrypt_mode = sec->param.encrypt_mode;
}
done:
if (req && (status != MLAN_STATUS_PENDING))
kfree(req);
LEAVE();
return status;
}
/**
* @brief Set wpa enable
*
* @param priv A pointer to moal_private structure
* @param wait_option Wait option
* @param enable MTRUE or MFALSE
*
* @return MLAN_STATUS_SUCCESS -- success, otherwise fail
*/
mlan_status
woal_set_wpa_enable(moal_private * priv, t_u8 wait_option, t_u32 enable)
{
int ret = 0;
mlan_ioctl_req *req = NULL;
mlan_ds_sec_cfg *sec = NULL;
mlan_status status = MLAN_STATUS_SUCCESS;
ENTER();
/* Allocate an IOCTL request buffer */
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg));
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
/* Fill request buffer */
sec = (mlan_ds_sec_cfg *) req->pbuf;
sec->sub_command = MLAN_OID_SEC_CFG_WPA_ENABLED;
req->req_id = MLAN_IOCTL_SEC_CFG;
req->action = MLAN_ACT_SET;
sec->param.wpa_enabled = enable;
/* Send IOCTL request to MLAN */
status = woal_request_ioctl(priv, req, wait_option);
done:
if (req && (status != MLAN_STATUS_PENDING))
kfree(req);
LEAVE();
return status;
}
/**
* @brief Get WPA enable
*
* @param priv A pointer to moal_private structure
* @param wait_option Wait option
* @param enable A pointer to wpa enable status
*
* @return MLAN_STATUS_SUCCESS -- success, otherwise fail
*/
mlan_status
woal_get_wpa_enable(moal_private * priv, t_u8 wait_option, t_u32 * enable)
{
int ret = 0;
mlan_ioctl_req *req = NULL;
mlan_ds_sec_cfg *sec = NULL;
mlan_status status = MLAN_STATUS_SUCCESS;
ENTER();
/* Allocate an IOCTL request buffer */
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg));
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
/* Fill request buffer */
sec = (mlan_ds_sec_cfg *) req->pbuf;
sec->sub_command = MLAN_OID_SEC_CFG_WPA_ENABLED;
req->req_id = MLAN_IOCTL_SEC_CFG;
req->action = MLAN_ACT_GET;
/* Send IOCTL request to MLAN */
status = woal_request_ioctl(priv, req, wait_option);
if (status == MLAN_STATUS_SUCCESS && enable) {
*enable = sec->param.wpa_enabled;
}
done:
if (req && (status != MLAN_STATUS_PENDING))
kfree(req);
LEAVE();
return status;
}
/**
* @brief Find the best network to associate
*
* @param priv A pointer to moal_private structure
* @param wait_option Wait option
* @param ssid_bssid A pointer to mlan_ssid_bssid structure
*
* @return MLAN_STATUS_SUCCESS -- success, otherwise fail
*/
mlan_status
woal_find_best_network(moal_private * priv, t_u8 wait_option,
mlan_ssid_bssid * ssid_bssid)
{
mlan_ioctl_req *req = NULL;
mlan_ds_bss *bss = NULL;
mlan_status ret = MLAN_STATUS_SUCCESS;
t_u8 *mac = 0;
ENTER();
if (!ssid_bssid) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
/* Allocate an IOCTL request buffer */
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss));
if (req == NULL) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
/* Fill request buffer */
bss = (mlan_ds_bss *) req->pbuf;
req->req_id = MLAN_IOCTL_BSS;
req->action = MLAN_ACT_GET;
bss->sub_command = MLAN_OID_BSS_FIND_BSS;
memcpy(&bss->param.ssid_bssid, ssid_bssid, sizeof(mlan_ssid_bssid));
/* Send IOCTL request to MLAN */
ret = woal_request_ioctl(priv, req, wait_option);
if (ret == MLAN_STATUS_SUCCESS) {
memcpy(ssid_bssid, &bss->param.ssid_bssid, sizeof(mlan_ssid_bssid));
mac = (t_u8 *) & ssid_bssid->bssid;
PRINTM(MCMND, "Find network: ssid=%s, %02x:%02x:%02x:%02x:%02x:%02x\n",
ssid_bssid->ssid.ssid, mac[0], mac[1], mac[2], mac[3], mac[4],
mac[5]);
}
done:
if (req)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief Request a scan
*
* @param priv A pointer to moal_private structure
* @param wait_option Wait option
* @param req_ssid A pointer to mlan_802_11_ssid structure
*
* @return MLAN_STATUS_SUCCESS -- success, otherwise fail
*/
mlan_status
woal_request_scan(moal_private * priv,
t_u8 wait_option, mlan_802_11_ssid * req_ssid)
{
mlan_status ret = MLAN_STATUS_SUCCESS;
mlan_ioctl_req *ioctl_req = NULL;
mlan_ds_scan *scan = NULL;
mlan_status status = MLAN_STATUS_SUCCESS;
ENTER();
if (MOAL_ACQ_SEMAPHORE_BLOCK(&priv->async_sem)) {
PRINTM(MERROR, "Acquire semaphore error, request_scan\n");
LEAVE();
return MLAN_STATUS_FAILURE;
}
priv->scan_pending_on_block = MTRUE;
/* Allocate an IOCTL request buffer */
ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_scan));
if (ioctl_req == NULL) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
scan = (mlan_ds_scan *) ioctl_req->pbuf;
if (req_ssid && req_ssid->ssid_len != 0) {
/* Specific SSID scan */
ioctl_req->req_id = MLAN_IOCTL_SCAN;
ioctl_req->action = MLAN_ACT_SET;
scan->sub_command = MLAN_OID_SCAN_SPECIFIC_SSID;
memcpy(scan->param.scan_req.scan_ssid.ssid,
req_ssid->ssid, req_ssid->ssid_len);
scan->param.scan_req.scan_ssid.ssid_len = req_ssid->ssid_len;
} else {
/* Normal scan */
ioctl_req->req_id = MLAN_IOCTL_SCAN;
ioctl_req->action = MLAN_ACT_SET;
scan->sub_command = MLAN_OID_SCAN_NORMAL;
}
/* Send IOCTL request to MLAN */
status = woal_request_ioctl(priv, ioctl_req, wait_option);
if (status == MLAN_STATUS_FAILURE) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
done:
if ((ioctl_req) && (status != MLAN_STATUS_PENDING))
kfree(ioctl_req);
if (ret == MLAN_STATUS_FAILURE) {
priv->scan_pending_on_block = MFALSE;
MOAL_REL_SEMAPHORE(&priv->async_sem);
}
LEAVE();
return ret;
}
/**
* @brief Set ewpa mode
*
* @param priv A pointer to moal_private structure
* @param wait_option Wait option
* @param ssid_bssid A pointer to mlan_ssid_bssid structure
*
* @return MLAN_STATUS_SUCCESS -- success, otherwise fail
*/
mlan_status
woal_set_ewpa_mode(moal_private * priv, t_u8 wait_option,
mlan_ssid_bssid * ssid_bssid)
{
int ret = 0;
mlan_ioctl_req *req = NULL;
mlan_ds_sec_cfg *sec = NULL;
mlan_status status = MLAN_STATUS_SUCCESS;
ENTER();
/* Allocate an IOCTL request buffer */
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg));
if (req == NULL) {
ret = -ENOMEM;
goto error;
}
/* Fill request buffer */
sec = (mlan_ds_sec_cfg *) req->pbuf;
sec->sub_command = MLAN_OID_SEC_CFG_PASSPHRASE;
req->req_id = MLAN_IOCTL_SEC_CFG;
req->action = MLAN_ACT_GET;
/* Try Get All */
memset(&sec->param.passphrase, 0, sizeof(mlan_ds_passphrase));
memcpy(&sec->param.passphrase.ssid, &ssid_bssid->ssid,
sizeof(sec->param.passphrase.ssid));
memcpy(&sec->param.passphrase.bssid, &ssid_bssid->bssid,
MLAN_MAC_ADDR_LENGTH);
sec->param.passphrase.psk_type = MLAN_PSK_QUERY;
/* Send IOCTL request to MLAN */
if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, wait_option))
goto error;
sec->param.ewpa_enabled = MFALSE;
if (sec->param.passphrase.psk_type == MLAN_PSK_PASSPHRASE) {
if (sec->param.passphrase.psk.passphrase.passphrase_len > 0) {
sec->param.ewpa_enabled = MTRUE;
}
} else if (sec->param.passphrase.psk_type == MLAN_PSK_PMK)
sec->param.ewpa_enabled = MTRUE;
sec->sub_command = MLAN_OID_SEC_CFG_EWPA_ENABLED;
req->action = MLAN_ACT_SET;
/* Send IOCTL request to MLAN */
status = woal_request_ioctl(priv, req, wait_option);
error:
if (req && (status != MLAN_STATUS_PENDING))
kfree(req);
LEAVE();
return status;
}
/**
* @brief Change Adhoc Channel
*
* @param priv A pointer to moal_private structure
* @param channel The channel to be set.
*
* @return MLAN_STATUS_SUCCESS--success, MLAN_STATUS_FAILURE--fail
*/
mlan_status
woal_change_adhoc_chan(moal_private * priv, int channel)
{
mlan_status ret = MLAN_STATUS_SUCCESS;
mlan_bss_info bss_info;
mlan_ds_bss *bss = NULL;
mlan_ioctl_req *req = NULL;
ENTER();
memset(&bss_info, 0, sizeof(bss_info));
/* Get BSS information */
if (MLAN_STATUS_SUCCESS !=
woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info)) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
if (bss_info.bss_mode == MLAN_BSS_MODE_INFRA) {
ret = MLAN_STATUS_SUCCESS;
goto done;
}
/* Allocate an IOCTL request buffer */
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss));
if (req == NULL) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
/* Get current channel */
bss = (mlan_ds_bss *) req->pbuf;
bss->sub_command = MLAN_OID_IBSS_CHANNEL;
req->req_id = MLAN_IOCTL_BSS;
req->action = MLAN_ACT_GET;
/* Send IOCTL request to MLAN */
if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
if (bss->param.bss_chan.channel == channel) {
ret = MLAN_STATUS_SUCCESS;
goto done;
}
PRINTM(MCMND, "Updating Channel from %d to %d\n",
(int) bss->param.bss_chan.channel, channel);
if (bss_info.media_connected != MTRUE) {
ret = MLAN_STATUS_SUCCESS;
goto done;
}
/* Do disonnect */
bss->sub_command = MLAN_OID_BSS_STOP;
memset((t_u8 *) & bss->param.bssid, 0, ETH_ALEN);
/* Send IOCTL request to MLAN */
if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
/* Do specific SSID scanning */
if (MLAN_STATUS_SUCCESS !=
woal_request_scan(priv, MOAL_IOCTL_WAIT, &bss_info.ssid)) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
/* Start/Join Adhoc network */
bss->sub_command = MLAN_OID_BSS_START;
memset(&bss->param.ssid_bssid, 0, sizeof(mlan_ssid_bssid));
memcpy(&bss->param.ssid_bssid.ssid, &bss_info.ssid,
sizeof(mlan_802_11_ssid));
/* Send IOCTL request to MLAN */
if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
ret = MLAN_STATUS_FAILURE;
}
done:
if (req)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief enable wep key
*
* @param priv A pointer to moal_private structure
* @param wait_option Wait option
*
* @return MLAN_STATUS_SUCCESS -- success, otherwise fail
*/
mlan_status
woal_enable_wep_key(moal_private * priv, t_u8 wait_option)
{
int ret = 0;
mlan_ioctl_req *req = NULL;
mlan_ds_sec_cfg *sec = NULL;
mlan_status status = MLAN_STATUS_SUCCESS;
ENTER();
/* Allocate an IOCTL request buffer */
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg));
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
/* Fill request buffer */
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.key_disable = MFALSE;
sec->param.encrypt_key.key_len = 0;
sec->param.encrypt_key.is_current_wep_key = MTRUE;
/* Send IOCTL request to MLAN */
status = woal_request_ioctl(priv, req, wait_option);
done:
if (req && (status != MLAN_STATUS_PENDING))
kfree(req);
LEAVE();
return status;
}
#if WIRELESS_EXT > 14
/**
* @brief This function sends customized event to application.
*
* @param priv A pointer to moal_private structure
* @param str A pointer to event string
*
* @return N/A
*/
void
woal_send_iwevcustom_event(moal_private * priv, t_s8 * str)
{
union iwreq_data iwrq;
char buf[50];
ENTER();
memset(&iwrq, 0, sizeof(union iwreq_data));
memset(buf, 0, sizeof(buf));
snprintf(buf, sizeof(buf) - 1, "%s", str);
iwrq.data.pointer = buf;
iwrq.data.length = strlen(buf) + 1 + IW_EV_LCP_LEN;
/* Send Event to upper layer */
wireless_send_event(priv->netdev, IWEVCUSTOM, &iwrq, buf);
PRINTM(MINFO, "Wireless event %s is sent to application\n", str);
LEAVE();
return;
}
#endif
#if WIRELESS_EXT >= 18
/**
* @brief This function sends mic error event to application.
*
* @param priv A pointer to moal_private structure
* @param event MIC MERROR EVENT.
*
* @return N/A
*/
void
woal_send_mic_error_event(moal_private * priv, t_u32 event)
{
union iwreq_data iwrq;
struct iw_michaelmicfailure mic;
ENTER();
memset(&iwrq, 0, sizeof(iwrq));
memset(&mic, 0, sizeof(mic));
if (event == MLAN_EVENT_ID_FW_MIC_ERR_UNI)
mic.flags = IW_MICFAILURE_PAIRWISE;
else
mic.flags = IW_MICFAILURE_GROUP;
iwrq.data.pointer = &mic;
iwrq.data.length = sizeof(mic);
wireless_send_event(priv->netdev, IWEVMICHAELMICFAILURE, &iwrq,
(char *) &mic);
LEAVE();
return;
}
#endif
/**
* @brief Handle ioctl resp
*
* @param priv Pointer to moal_private structure
* @param req Pointer to mlan_ioctl_req structure
*
* @return N/A
*/
void
woal_process_ioctl_resp(moal_private * priv, mlan_ioctl_req * req)
{
ENTER();
if (priv == NULL)
return;
switch (req->req_id) {
case MLAN_IOCTL_GET_INFO:
woal_ioctl_get_info_resp(priv, (mlan_ds_get_info *) req->pbuf);
break;
case MLAN_IOCTL_BSS:
woal_ioctl_get_bss_resp(priv, (mlan_ds_bss *) req->pbuf);
default:
break;
}
LEAVE();
return;
}