mirror of
https://github.com/nxp-imx/mwifiex.git
synced 2024-11-14 19:15:35 +00:00
13b6fdb0b4
changes: 1. WCSWREL-191: Fixed the error when loading module param from user config for SD8801 2. WCSWREL-186: Fixed the issue of mlanutl failing on kernel higher than L5.15 3. Fixed low throughput issue for WPA3 SAE 4. Added driver change for WLAN throughput improvement on 8997 SoC 5. Updated README to recommend not to use WEP/TKIP for all chipsets 6. WCSWREL-180: Fix P2P test fail on kernel higher than L5.12 7. WCSWREL-156: kernel_write/kernel_read not allowed by drivers for L5.10 kernel GKI buildou 8. Alternative for pm_qos_add_request/pm_qos_remove_request Signed-off-by: Sherry Sun <sherry.sun@nxp.com> Approved-by: Tian Yang <yang.tian@nxp.com>
6913 lines
174 KiB
C
6913 lines
174 KiB
C
/** @file moal_priv.c
|
|
*
|
|
* @brief This file contains standard ioctl functions
|
|
*
|
|
*
|
|
* Copyright 2008-2021 NXP
|
|
*
|
|
* This software file (the File) is distributed by NXP
|
|
* under the terms of the GNU General Public License Version 2, June 1991
|
|
* (the License). You may use, redistribute and/or modify the File in
|
|
* accordance with the terms and conditions of the License, a copy of which
|
|
* is available by writing to the Free Software Foundation, Inc.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
|
|
* worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
|
|
*
|
|
* THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE EXPRESSLY DISCLAIMED. The License provides additional details about
|
|
* this warranty disclaimer.
|
|
*
|
|
*/
|
|
|
|
/************************************************************************
|
|
Change log:
|
|
10/30/2008: initial version
|
|
************************************************************************/
|
|
|
|
#include "moal_main.h"
|
|
#ifdef SDIO
|
|
#include "moal_sdio.h"
|
|
#endif /* SDIO */
|
|
|
|
#include "moal_eth_ioctl.h"
|
|
#ifdef USB
|
|
#include "moal_usb.h"
|
|
#endif
|
|
|
|
/********************************************************
|
|
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,
|
|
BAND_GN | BAND_GAC,
|
|
BAND_B | BAND_G | BAND_GN | BAND_GAC,
|
|
BAND_G | BAND_GN | BAND_GAC,
|
|
BAND_A | BAND_B | BAND_G | BAND_AN | BAND_GN | BAND_AAC,
|
|
BAND_A | BAND_G | BAND_AN | BAND_GN | BAND_AAC,
|
|
BAND_A | BAND_AN | BAND_AAC,
|
|
};
|
|
|
|
/** Bands supported in Ad-Hoc mode */
|
|
static t_u8 SupportedAdhocBand[] = {
|
|
BAND_B,
|
|
BAND_B | BAND_G,
|
|
BAND_G,
|
|
BAND_A,
|
|
};
|
|
|
|
/********************************************************
|
|
Local Functions
|
|
********************************************************/
|
|
|
|
/**
|
|
* @brief Associated to a specific indexed entry in the ScanTable
|
|
*
|
|
* @param priv A pointer to moal_private structure
|
|
* @param req A pointer to ifreq structure
|
|
*
|
|
* @return 0 --success, otherwise fail
|
|
*/
|
|
static int woal_associate_ssid_bssid(moal_private *priv, struct iwreq *wrq)
|
|
{
|
|
mlan_ssid_bssid ssid_bssid;
|
|
#ifdef REASSOCIATION
|
|
mlan_bss_info bss_info;
|
|
#endif
|
|
char buf[64];
|
|
t_u8 buflen;
|
|
t_u8 mac_idx;
|
|
t_u8 i;
|
|
|
|
ENTER();
|
|
|
|
memset(&ssid_bssid, 0, sizeof(ssid_bssid));
|
|
mac_idx = 0;
|
|
buflen = MIN(wrq->u.data.length, (sizeof(buf) - 1));
|
|
memset(buf, 0, sizeof(buf));
|
|
|
|
if (buflen < (3 * ETH_ALEN) + 2) {
|
|
PRINTM(MERROR,
|
|
"Associate: Insufficient length in IOCTL input\n");
|
|
|
|
/* buffer should be at least 3 characters per BSSID octet "00:"
|
|
** plus a space separater and at least 1 char in the SSID
|
|
*/
|
|
LEAVE();
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (copy_from_user(buf, wrq->u.data.pointer, buflen) != 0) {
|
|
/* copy_from_user failed */
|
|
PRINTM(MERROR, "Associate: copy from user failed\n");
|
|
LEAVE();
|
|
return -EINVAL;
|
|
}
|
|
|
|
for (i = 0; (i < buflen) && (buf[i] == ' '); i++) {
|
|
/* Skip white space */
|
|
}
|
|
|
|
/* Copy/Convert the BSSID */
|
|
for (; (i < buflen) && (mac_idx < ETH_ALEN) && (buf[i] != ' '); i++) {
|
|
if (buf[i] == ':') {
|
|
mac_idx++;
|
|
} else {
|
|
if (mac_idx < ETH_ALEN)
|
|
ssid_bssid.bssid[mac_idx] =
|
|
(t_u8)woal_atox(buf + i);
|
|
|
|
while ((i < buflen) && (isxdigit(buf[i + 1]))) {
|
|
/* Skip entire hex value */
|
|
i++;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Skip one space between the BSSID and start of the SSID */
|
|
i++;
|
|
|
|
/* Copy the SSID */
|
|
ssid_bssid.ssid.ssid_len = buflen - i - 1;
|
|
moal_memcpy_ext(priv->phandle, ssid_bssid.ssid.ssid, buf + i,
|
|
sizeof(ssid_bssid.ssid.ssid),
|
|
sizeof(ssid_bssid.ssid.ssid));
|
|
|
|
PRINTM(MCMND, "iwpriv assoc: AP=[" MACSTR "], ssid(%d)=[%s]\n",
|
|
MAC2STR(ssid_bssid.bssid), (int)ssid_bssid.ssid.ssid_len,
|
|
ssid_bssid.ssid.ssid);
|
|
|
|
if (MLAN_STATUS_SUCCESS !=
|
|
woal_bss_start(priv, MOAL_IOCTL_WAIT, &ssid_bssid)) {
|
|
LEAVE();
|
|
return -EFAULT;
|
|
}
|
|
|
|
#ifdef REASSOCIATION
|
|
memset(&bss_info, 0x00, sizeof(bss_info));
|
|
if (MLAN_STATUS_SUCCESS ==
|
|
woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info)) {
|
|
moal_memcpy_ext(priv->phandle, &priv->prev_ssid_bssid.ssid,
|
|
&bss_info.ssid, sizeof(mlan_802_11_ssid),
|
|
sizeof(mlan_802_11_ssid));
|
|
moal_memcpy_ext(priv->phandle, &priv->prev_ssid_bssid.bssid,
|
|
&bss_info.bssid, MLAN_MAC_ADDR_LENGTH,
|
|
sizeof(mlan_802_11_mac_addr));
|
|
}
|
|
#endif /* REASSOCIATION */
|
|
|
|
LEAVE();
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @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 Performs warm reset
|
|
*
|
|
* @param priv A pointer to moal_private structure
|
|
*
|
|
* @return 0/MLAN_STATUS_SUCCESS --success, otherwise fail
|
|
*/
|
|
static int woal_warm_reset(moal_private *priv)
|
|
{
|
|
int ret = 0;
|
|
moal_handle *handle = priv->phandle;
|
|
moal_handle *ref_handle;
|
|
moal_private *ref_priv;
|
|
ENTER();
|
|
ret = woal_pre_warmreset(priv);
|
|
if (ret)
|
|
goto done;
|
|
ref_handle = (moal_handle *)handle->pref_mac;
|
|
if (ref_handle) {
|
|
ref_priv = woal_get_priv(ref_handle, MLAN_BSS_ROLE_ANY);
|
|
if (ref_priv) {
|
|
ret = woal_pre_warmreset(ref_priv);
|
|
if (ret)
|
|
goto done;
|
|
ret = woal_warmreset(ref_priv);
|
|
if (ret)
|
|
goto done;
|
|
}
|
|
}
|
|
ret = woal_warmreset(priv);
|
|
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;
|
|
int buflen = 0;
|
|
|
|
ENTER();
|
|
|
|
memset(in_data, 0, sizeof(in_data));
|
|
memset(out_data, 0, sizeof(out_data));
|
|
buflen = MIN(wrq->u.data.length, IN_DATA_SIZE);
|
|
|
|
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 (sizeof(int) * wrq->u.data.length > sizeof(in_data)) {
|
|
PRINTM(MERROR, "Too many arguments\n");
|
|
ret = -EINVAL;
|
|
goto done;
|
|
}
|
|
if (copy_from_user(in_data, wrq->u.data.pointer,
|
|
sizeof(int) * buflen)) {
|
|
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/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)
|
|
{
|
|
int ret = 0;
|
|
int user_data_len;
|
|
t_u32 deep_sleep = DEEP_SLEEP_OFF;
|
|
t_u32 data[2] = {0};
|
|
int copy_len;
|
|
t_u16 idletime = DEEP_SLEEP_IDLE_TIME;
|
|
|
|
ENTER();
|
|
|
|
user_data_len = wrq->u.data.length;
|
|
copy_len = MIN(sizeof(data), sizeof(int) * user_data_len);
|
|
if (user_data_len == 1 || user_data_len == 2) {
|
|
if (copy_from_user(&data, wrq->u.data.pointer, copy_len)) {
|
|
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");
|
|
ret = woal_set_deep_sleep(priv, MOAL_IOCTL_WAIT, MFALSE,
|
|
0);
|
|
if (ret != MLAN_STATUS_SUCCESS) {
|
|
LEAVE();
|
|
return -EINVAL;
|
|
}
|
|
} else if (deep_sleep == DEEP_SLEEP_ON) {
|
|
PRINTM(MINFO, "Enter Deep Sleep Mode\n");
|
|
if (user_data_len == 2)
|
|
idletime = data[1];
|
|
else
|
|
idletime = 0;
|
|
ret = woal_set_deep_sleep(priv, MOAL_IOCTL_WAIT, MTRUE,
|
|
idletime);
|
|
if (ret != MLAN_STATUS_SUCCESS) {
|
|
LEAVE();
|
|
return -EINVAL;
|
|
}
|
|
} else {
|
|
PRINTM(MERROR, "Unknown option = %u\n", deep_sleep);
|
|
LEAVE();
|
|
return -EINVAL;
|
|
}
|
|
} else if (user_data_len > 2) {
|
|
PRINTM(MERROR, "Invalid number of arguments %d\n",
|
|
user_data_len);
|
|
LEAVE();
|
|
return -EINVAL;
|
|
} else { /* Display Deep Sleep settings */
|
|
PRINTM(MINFO, "Get Deep Sleep Mode\n");
|
|
if (MLAN_STATUS_SUCCESS != woal_get_deep_sleep(priv, data)) {
|
|
LEAVE();
|
|
return -EFAULT;
|
|
}
|
|
if (data[0] == 0)
|
|
wrq->u.data.length = 1;
|
|
else
|
|
wrq->u.data.length = 2;
|
|
}
|
|
|
|
/* Copy the Deep Sleep setting to user */
|
|
if (copy_to_user(wrq->u.data.pointer, data,
|
|
wrq->u.data.length * sizeof(int))) {
|
|
PRINTM(MERROR, "Copy to user failed\n");
|
|
LEAVE();
|
|
return -EINVAL;
|
|
}
|
|
|
|
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[2], copy_len;
|
|
mlan_ioctl_req *req = NULL;
|
|
mlan_ds_11n_cfg *cfg_11n = NULL;
|
|
int ret = 0;
|
|
int data_length = wrq->u.data.length;
|
|
mlan_status status = MLAN_STATUS_SUCCESS;
|
|
|
|
ENTER();
|
|
|
|
if (data_length > 2) {
|
|
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_HTCAP_CFG;
|
|
req->req_id = MLAN_IOCTL_11N_CFG;
|
|
|
|
copy_len = MIN(sizeof(data), sizeof(int) * data_length);
|
|
if (data_length == 0) {
|
|
/* Get 11n tx parameters from MLAN */
|
|
req->action = MLAN_ACT_GET;
|
|
cfg_11n->param.htcap_cfg.misc_cfg = BAND_SELECT_BG;
|
|
} else {
|
|
if (copy_from_user(data, wrq->u.data.pointer, copy_len)) {
|
|
PRINTM(MERROR, "Copy from user failed\n");
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
|
|
cfg_11n->param.htcap_cfg.htcap = data[0];
|
|
PRINTM(MINFO, "SET: htcapinfo:0x%x\n", data[0]);
|
|
cfg_11n->param.htcap_cfg.misc_cfg = BAND_SELECT_BOTH;
|
|
if (data_length == 2) {
|
|
if (data[1] != BAND_SELECT_BG &&
|
|
data[1] != BAND_SELECT_A &&
|
|
data[1] != BAND_SELECT_BOTH) {
|
|
PRINTM(MERROR, "Invalid band selection\n");
|
|
ret = -EINVAL;
|
|
goto done;
|
|
}
|
|
cfg_11n->param.htcap_cfg.misc_cfg = data[1];
|
|
PRINTM(MINFO, "SET: htcapinfo band:0x%x\n", data[1]);
|
|
}
|
|
/* Update 11n tx parameters in MLAN */
|
|
req->action = MLAN_ACT_SET;
|
|
}
|
|
|
|
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
|
|
if (status != MLAN_STATUS_SUCCESS) {
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
data[0] = cfg_11n->param.htcap_cfg.htcap;
|
|
|
|
if (req->action == MLAN_ACT_GET) {
|
|
data_length = 1;
|
|
cfg_11n->param.htcap_cfg.htcap = 0;
|
|
cfg_11n->param.htcap_cfg.misc_cfg = BAND_SELECT_A;
|
|
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
|
|
if (status != MLAN_STATUS_SUCCESS) {
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
if ((int)cfg_11n->param.htcap_cfg.htcap != data[0]) {
|
|
data_length = 2;
|
|
data[1] = cfg_11n->param.htcap_cfg.htcap;
|
|
PRINTM(MINFO, "GET: htcapinfo for 2.4GHz:0x%x\n",
|
|
data[0]);
|
|
PRINTM(MINFO, "GET: htcapinfo for 5GHz:0x%x\n",
|
|
data[1]);
|
|
} else
|
|
PRINTM(MINFO, "GET: htcapinfo:0x%x\n", data[0]);
|
|
}
|
|
|
|
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 = data_length;
|
|
|
|
done:
|
|
if (status != MLAN_STATUS_PENDING)
|
|
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], copy_len;
|
|
mlan_ioctl_req *req = NULL;
|
|
mlan_ds_11n_cfg *cfg_11n = NULL;
|
|
int ret = 0;
|
|
int data_length = wrq->u.data.length;
|
|
mlan_status status = MLAN_STATUS_SUCCESS;
|
|
|
|
ENTER();
|
|
|
|
if ((data_length != 0) && (data_length != 1)) {
|
|
PRINTM(MERROR, "Invalid number of arguments\n");
|
|
ret = -EINVAL;
|
|
goto done;
|
|
}
|
|
copy_len = MIN(sizeof(data), sizeof(int) * data_length);
|
|
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 (data_length == 0) {
|
|
/* Get 11n tx parameters from MLAN */
|
|
req->action = MLAN_ACT_GET;
|
|
} else if (data_length == 1) {
|
|
if (copy_from_user(data, wrq->u.data.pointer, copy_len)) {
|
|
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;
|
|
}
|
|
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
|
|
if (status != MLAN_STATUS_SUCCESS) {
|
|
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 (status != MLAN_STATUS_PENDING)
|
|
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[2], copy_len;
|
|
mlan_ioctl_req *req = NULL;
|
|
mlan_ds_11n_cfg *cfg_11n = NULL;
|
|
int ret = 0;
|
|
int data_length = wrq->u.data.length;
|
|
mlan_status status = MLAN_STATUS_SUCCESS;
|
|
|
|
ENTER();
|
|
|
|
if (data_length > 2) {
|
|
PRINTM(MERROR, "Invalid number of arguments\n");
|
|
ret = -EINVAL;
|
|
goto done;
|
|
}
|
|
copy_len = MIN(sizeof(data), sizeof(int) * data_length);
|
|
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 (data_length == 0) {
|
|
/* Get 11n tx parameters from MLAN */
|
|
req->action = MLAN_ACT_GET;
|
|
cfg_11n->param.tx_cfg.misc_cfg = BAND_SELECT_BG;
|
|
} else {
|
|
if (copy_from_user(data, wrq->u.data.pointer, copy_len)) {
|
|
PRINTM(MERROR, "Copy from user failed\n");
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
|
|
cfg_11n->param.tx_cfg.httxcap = data[0];
|
|
PRINTM(MINFO, "SET: httxcap:0x%x\n", data[0]);
|
|
cfg_11n->param.tx_cfg.misc_cfg = BAND_SELECT_BOTH;
|
|
if (data_length == 2) {
|
|
if (data[1] != BAND_SELECT_BG &&
|
|
data[1] != BAND_SELECT_A &&
|
|
data[1] != BAND_SELECT_BOTH) {
|
|
PRINTM(MERROR, "Invalid band selection\n");
|
|
ret = -EINVAL;
|
|
goto done;
|
|
}
|
|
cfg_11n->param.tx_cfg.misc_cfg = data[1];
|
|
PRINTM(MINFO, "SET: httxcap band:0x%x\n", data[1]);
|
|
}
|
|
/* Update 11n tx parameters in MLAN */
|
|
req->action = MLAN_ACT_SET;
|
|
}
|
|
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
|
|
if (status != MLAN_STATUS_SUCCESS) {
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
data[0] = cfg_11n->param.tx_cfg.httxcap;
|
|
|
|
if (req->action == MLAN_ACT_GET) {
|
|
data_length = 1;
|
|
cfg_11n->param.tx_cfg.httxcap = 0;
|
|
cfg_11n->param.tx_cfg.misc_cfg = BAND_SELECT_A;
|
|
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
|
|
if (status != MLAN_STATUS_SUCCESS) {
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
if (cfg_11n->param.tx_cfg.httxcap != data[0]) {
|
|
data_length = 2;
|
|
data[1] = cfg_11n->param.tx_cfg.httxcap;
|
|
PRINTM(MINFO, "GET: httxcap for 2.4GHz:0x%x\n",
|
|
data[0]);
|
|
PRINTM(MINFO, "GET: httxcap for 5GHz:0x%x\n", data[1]);
|
|
} else
|
|
PRINTM(MINFO, "GET: httxcap:0x%x\n", data[0]);
|
|
}
|
|
|
|
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 = data_length;
|
|
|
|
done:
|
|
if (status != MLAN_STATUS_PENDING)
|
|
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, copy_len;
|
|
mlan_ioctl_req *req = NULL;
|
|
mlan_ds_11n_cfg *cfg_11n = NULL;
|
|
int ret = 0;
|
|
int data_length = wrq->u.data.length;
|
|
mlan_status status = MLAN_STATUS_SUCCESS;
|
|
|
|
ENTER();
|
|
|
|
if (wrq->u.data.pointer == NULL) {
|
|
LEAVE();
|
|
return -EINVAL;
|
|
}
|
|
copy_len = MIN(sizeof(data), sizeof(int) * data_length);
|
|
|
|
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 (data_length == 0) {
|
|
/* Get aggr priority table from MLAN */
|
|
req->action = MLAN_ACT_GET;
|
|
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
|
|
if (status != MLAN_STATUS_SUCCESS) {
|
|
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 (data_length == 16) {
|
|
if (copy_from_user(data, wrq->u.data.pointer, copy_len)) {
|
|
PRINTM(MERROR, "Copy from user failed\n");
|
|
ret = -EFAULT;
|
|
goto error;
|
|
}
|
|
for (i = 0, j = 0; i < (data_length); i = i + 2, ++j) {
|
|
if ((data[i] > 7 && data[i] != 0xff) ||
|
|
(data[i + 1] > 7 && data[i + 1] != 0xff)) {
|
|
PRINTM(MERROR,
|
|
"Invalid priority, valid value 0-7 or 0xff.\n");
|
|
ret = -EFAULT;
|
|
goto error;
|
|
}
|
|
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;
|
|
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
|
|
if (status != MLAN_STATUS_SUCCESS) {
|
|
ret = -EFAULT;
|
|
goto error;
|
|
}
|
|
} else {
|
|
PRINTM(MERROR, "Invalid number of arguments\n");
|
|
ret = -EINVAL;
|
|
goto error;
|
|
}
|
|
|
|
error:
|
|
if (status != MLAN_STATUS_PENDING)
|
|
kfree(req);
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Set/Get add BA Reject parameters
|
|
*
|
|
* @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, copy_len;
|
|
mlan_ioctl_req *req = NULL;
|
|
mlan_ds_11n_cfg *cfg_11n = NULL;
|
|
int data_length = wrq->u.data.length;
|
|
mlan_status status = MLAN_STATUS_SUCCESS;
|
|
ENTER();
|
|
|
|
copy_len = MIN(sizeof(data), sizeof(int) * data_length);
|
|
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 (data_length == 0) {
|
|
PRINTM(MERROR, "Addba reject moal\n");
|
|
/* Get aggr priority table from MLAN */
|
|
req->action = MLAN_ACT_GET;
|
|
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
|
|
if (status != MLAN_STATUS_SUCCESS) {
|
|
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 (data_length == 8) {
|
|
if (copy_from_user(data, wrq->u.data.pointer, copy_len)) {
|
|
PRINTM(MERROR, "Copy from user failed\n");
|
|
ret = -EFAULT;
|
|
goto error;
|
|
}
|
|
for (i = 0; i < (data_length); ++i) {
|
|
if (data[i] != 0 && data[i] != 1) {
|
|
PRINTM(MERROR,
|
|
"addba reject only takes argument as 0 or 1\n");
|
|
ret = -EFAULT;
|
|
goto error;
|
|
}
|
|
cfg_11n->param.addba_reject[i] = data[i];
|
|
}
|
|
|
|
/* Update aggr priority table in MLAN */
|
|
req->action = MLAN_ACT_SET;
|
|
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
|
|
if (status != MLAN_STATUS_SUCCESS) {
|
|
ret = -EFAULT;
|
|
goto error;
|
|
}
|
|
} else {
|
|
PRINTM(MERROR, "Invalid number of arguments\n");
|
|
ret = -EINVAL;
|
|
goto error;
|
|
}
|
|
error:
|
|
if (status != MLAN_STATUS_PENDING)
|
|
kfree(req);
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Set/Get add BA parameters
|
|
*
|
|
* @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[5], ret = 0, copy_len;
|
|
mlan_ioctl_req *req = NULL;
|
|
mlan_ds_11n_cfg *cfg_11n = NULL;
|
|
int data_length = wrq->u.data.length;
|
|
mlan_status status = MLAN_STATUS_SUCCESS;
|
|
|
|
ENTER();
|
|
|
|
copy_len = MIN(sizeof(data), sizeof(int) * data_length);
|
|
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 (data_length == 0) {
|
|
/* Get Add BA parameters from MLAN */
|
|
req->action = MLAN_ACT_GET;
|
|
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
|
|
if (status != MLAN_STATUS_SUCCESS) {
|
|
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;
|
|
data[3] = cfg_11n->param.addba_param.txamsdu;
|
|
data[4] = cfg_11n->param.addba_param.rxamsdu;
|
|
PRINTM(MINFO,
|
|
"GET: timeout:%d txwinsize:%d rxwinsize:%d txamsdu=%d, rxamsdu=%d\n",
|
|
data[0], data[1], data[2], data[3], data[4]);
|
|
wrq->u.data.length = 5;
|
|
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 (data_length == 5) {
|
|
if (copy_from_user(data, wrq->u.data.pointer, copy_len)) {
|
|
PRINTM(MERROR, "Copy from user failed\n");
|
|
ret = -EFAULT;
|
|
goto error;
|
|
}
|
|
if (data[0] < 0 || data[0] > MLAN_DEFAULT_BLOCK_ACK_TIMEOUT) {
|
|
PRINTM(MERROR, "Incorrect addba timeout value.\n");
|
|
ret = -EFAULT;
|
|
goto error;
|
|
}
|
|
if (data[1] <= 0 || data[1] > MLAN_AMPDU_MAX_TXWINSIZE ||
|
|
data[2] <= 0 || data[2] > MLAN_AMPDU_MAX_RXWINSIZE) {
|
|
PRINTM(MERROR, "Incorrect Tx/Rx window size.\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];
|
|
if (data[3] < 0 || data[3] > 1 || data[4] < 0 || data[4] > 1) {
|
|
PRINTM(MERROR, "Incorrect Tx/Rx amsdu.\n");
|
|
ret = -EFAULT;
|
|
goto error;
|
|
}
|
|
cfg_11n->param.addba_param.txamsdu = data[3];
|
|
cfg_11n->param.addba_param.rxamsdu = data[4];
|
|
PRINTM(MINFO,
|
|
"SET: timeout:%d txwinsize:%d rxwinsize:%d txamsdu=%d rxamsdu=%d\n",
|
|
data[0], data[1], data[2], data[3], data[4]);
|
|
/* Update Add BA parameters in MLAN */
|
|
req->action = MLAN_ACT_SET;
|
|
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
|
|
if (status != MLAN_STATUS_SUCCESS) {
|
|
ret = MLAN_STATUS_FAILURE;
|
|
goto error;
|
|
}
|
|
} else {
|
|
PRINTM(MERROR, "Invalid number of arguments\n");
|
|
ret = -EINVAL;
|
|
goto error;
|
|
}
|
|
|
|
error:
|
|
if (status != MLAN_STATUS_PENDING)
|
|
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;
|
|
mlan_status status = MLAN_STATUS_SUCCESS;
|
|
|
|
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 {
|
|
ret = -EINVAL;
|
|
PRINTM(MERROR,
|
|
"Don't support set Tx buffer size after driver loaded!\n");
|
|
goto done;
|
|
}
|
|
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
|
|
if (status != MLAN_STATUS_SUCCESS) {
|
|
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 (status != MLAN_STATUS_PENDING)
|
|
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], copy_len;
|
|
int ret = 0;
|
|
mlan_ds_hs_cfg hscfg;
|
|
t_u16 action;
|
|
mlan_bss_info bss_info;
|
|
int data_length = wrq->u.data.length;
|
|
|
|
ENTER();
|
|
|
|
memset(data, 0, sizeof(data));
|
|
memset(&hscfg, 0, sizeof(mlan_ds_hs_cfg));
|
|
copy_len = MIN(sizeof(data), sizeof(int) * data_length);
|
|
|
|
if (data_length == 0) {
|
|
action = MLAN_ACT_GET;
|
|
} else {
|
|
action = MLAN_ACT_SET;
|
|
if (data_length >= 1 && data_length <= 3) {
|
|
if (copy_from_user(data, wrq->u.data.pointer,
|
|
copy_len)) {
|
|
PRINTM(MERROR, "Copy from user failed\n");
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
} else {
|
|
PRINTM(MERROR, "Invalid arguments\n");
|
|
ret = -EINVAL;
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
/* HS config is blocked if HS is already activated */
|
|
if (data_length && (data[0] != (int)HOST_SLEEP_CFG_CANCEL ||
|
|
invoke_hostcmd == MFALSE)) {
|
|
memset(&bss_info, 0, sizeof(bss_info));
|
|
woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info);
|
|
if (bss_info.is_hs_configured) {
|
|
PRINTM(MERROR, "HS already configured\n");
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
/* Do a GET first if some arguments are not provided */
|
|
if (data_length >= 1 && data_length < 3) {
|
|
woal_set_get_hs_params(priv, MLAN_ACT_GET, MOAL_IOCTL_WAIT,
|
|
&hscfg);
|
|
}
|
|
|
|
if (data_length)
|
|
hscfg.conditions = data[0];
|
|
if (data_length >= 2)
|
|
hscfg.gpio = data[1];
|
|
if (data_length == 3)
|
|
hscfg.gap = data[2];
|
|
|
|
if ((invoke_hostcmd == MTRUE) && (action == MLAN_ACT_SET)) {
|
|
/* Need to issue an extra IOCTL first to set up parameters */
|
|
hscfg.is_invoke_hostcmd = MFALSE;
|
|
if (MLAN_STATUS_SUCCESS !=
|
|
woal_set_get_hs_params(priv, MLAN_ACT_SET, MOAL_IOCTL_WAIT,
|
|
&hscfg)) {
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
}
|
|
hscfg.is_invoke_hostcmd = invoke_hostcmd;
|
|
if (MLAN_STATUS_SUCCESS !=
|
|
woal_set_get_hs_params(priv, action, MOAL_IOCTL_WAIT, &hscfg)) {
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
|
|
if (action == MLAN_ACT_GET) {
|
|
data[0] = hscfg.conditions;
|
|
data[1] = hscfg.gpio;
|
|
data[2] = hscfg.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:
|
|
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], copy_len;
|
|
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;
|
|
mlan_status status = MLAN_STATUS_SUCCESS;
|
|
|
|
ENTER();
|
|
|
|
copy_len = MIN(sizeof(data), sizeof(int) * data_length);
|
|
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) ||
|
|
sizeof(int) * data_length > sizeof(data)) {
|
|
PRINTM(MERROR, "Invalid number of parameters\n");
|
|
ret = -EINVAL;
|
|
goto done;
|
|
}
|
|
|
|
req->action = MLAN_ACT_GET;
|
|
if (data_length) {
|
|
if (copy_from_user(data, wrq->u.data.pointer, copy_len)) {
|
|
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];
|
|
}
|
|
|
|
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
|
|
if (status != MLAN_STATUS_SUCCESS) {
|
|
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 (status != MLAN_STATUS_PENDING)
|
|
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], copy_len;
|
|
int ret = 0;
|
|
mlan_ioctl_req *req = NULL;
|
|
mlan_ds_misc_cfg *cfg = NULL;
|
|
int data_length = wrq->u.data.length;
|
|
int i = 0;
|
|
mlan_status status = MLAN_STATUS_SUCCESS;
|
|
|
|
ENTER();
|
|
|
|
memset(data, 0, sizeof(data));
|
|
copy_len = MIN(sizeof(data), sizeof(int) * data_length);
|
|
|
|
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, copy_len)) {
|
|
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;
|
|
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
|
|
if (status != MLAN_STATUS_SUCCESS) {
|
|
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;
|
|
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
|
|
if (status != MLAN_STATUS_SUCCESS) {
|
|
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];
|
|
|
|
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
|
|
if (status != MLAN_STATUS_SUCCESS) {
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
}
|
|
done:
|
|
if (status != MLAN_STATUS_PENDING)
|
|
kfree(req);
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief 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;
|
|
unsigned int i;
|
|
int data[3];
|
|
int user_data_len = wrq->u.data.length, copy_len;
|
|
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;
|
|
mlan_status status = MLAN_STATUS_SUCCESS;
|
|
|
|
ENTER();
|
|
|
|
if (sizeof(int) * user_data_len > sizeof(data)) {
|
|
PRINTM(MERROR, "Too many arguments\n");
|
|
LEAVE();
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (user_data_len > 0) {
|
|
if (priv->media_connected == MTRUE) {
|
|
LEAVE();
|
|
return -EOPNOTSUPP;
|
|
}
|
|
}
|
|
|
|
copy_len = MIN(sizeof(data), sizeof(int) * user_data_len);
|
|
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_channel values
|
|
* from MLAN */
|
|
req->action = MLAN_ACT_GET;
|
|
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
|
|
if (status != MLAN_STATUS_SUCCESS) {
|
|
ret = -EFAULT;
|
|
goto error;
|
|
}
|
|
/* Infra Band */
|
|
data[0] = radio_cfg->param.band_cfg.config_bands;
|
|
/* Adhoc Band */
|
|
data[1] = radio_cfg->param.band_cfg.adhoc_start_band;
|
|
/* Adhoc Channel */
|
|
data[2] = radio_cfg->param.band_cfg.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, copy_len)) {
|
|
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;
|
|
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
|
|
if (status != MLAN_STATUS_SUCCESS) {
|
|
ret = -EFAULT;
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
error:
|
|
if (status != MLAN_STATUS_PENDING)
|
|
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], copy_len;
|
|
int ret = 0;
|
|
mlan_ioctl_req *req = NULL;
|
|
mlan_ds_reg_mem *reg = NULL;
|
|
int data_length = wrq->u.data.length;
|
|
mlan_status status = MLAN_STATUS_SUCCESS;
|
|
|
|
ENTER();
|
|
|
|
memset(data, 0, sizeof(data));
|
|
copy_len = MIN(sizeof(data), sizeof(int) * data_length);
|
|
|
|
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, copy_len)) {
|
|
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];
|
|
|
|
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
|
|
if (status != MLAN_STATUS_SUCCESS) {
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
|
|
if (req->action == MLAN_ACT_GET) {
|
|
if (copy_to_user(wrq->u.data.pointer, ®->param.reg_rw.value,
|
|
sizeof(int))) {
|
|
PRINTM(MERROR, "Copy to user failed\n");
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
wrq->u.data.length = 1;
|
|
}
|
|
|
|
done:
|
|
if (status != MLAN_STATUS_PENDING)
|
|
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], copy_len;
|
|
int ret = 0;
|
|
mlan_ioctl_req *req = NULL;
|
|
mlan_ds_reg_mem *reg = NULL;
|
|
int data_length = wrq->u.data.length;
|
|
mlan_status status = MLAN_STATUS_SUCCESS;
|
|
|
|
ENTER();
|
|
|
|
memset(data, 0, sizeof(data));
|
|
copy_len = MIN(sizeof(data), sizeof(int) * data_length);
|
|
|
|
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, copy_len)) {
|
|
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];
|
|
|
|
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
|
|
if (status != MLAN_STATUS_SUCCESS) {
|
|
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,
|
|
MIN(wrq->u.data.length, MAX_EEPROM_DATA))) {
|
|
PRINTM(MERROR, "Copy to user failed\n");
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
done:
|
|
if (status != MLAN_STATUS_PENDING)
|
|
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, copy_len;
|
|
mlan_status status = MLAN_STATUS_SUCCESS;
|
|
|
|
ENTER();
|
|
|
|
memset(data, 0, sizeof(data));
|
|
copy_len = MIN(sizeof(data), sizeof(int) * data_length);
|
|
|
|
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, copy_len)) {
|
|
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);
|
|
|
|
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
|
|
if (status != MLAN_STATUS_SUCCESS) {
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
|
|
if (req->action == MLAN_ACT_GET) {
|
|
if (copy_to_user(wrq->u.data.pointer,
|
|
®_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 (status != MLAN_STATUS_PENDING)
|
|
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;
|
|
int i = 0;
|
|
|
|
ENTER();
|
|
|
|
PRINTM(MINFO, " GET STATS\n");
|
|
buf = kmalloc(GETLOG_BUFSIZE, GFP_KERNEL);
|
|
if (!buf) {
|
|
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 %u\n"
|
|
"failed %u\n"
|
|
"retry %u\n"
|
|
"multiretry %u\n"
|
|
"framedup %u\n"
|
|
"rtssuccess %u\n"
|
|
"rtsfailure %u\n"
|
|
"ackfailure %u\n"
|
|
"rxfrag %u\n"
|
|
"mcastrxframe %u\n"
|
|
"fcserror %u\n"
|
|
"txframe %u\n"
|
|
"wepicverrcnt-1 %u\n"
|
|
"wepicverrcnt-2 %u\n"
|
|
"wepicverrcnt-3 %u\n"
|
|
"wepicverrcnt-4 %u\n"
|
|
"beacon_rcnt %u\n"
|
|
"beacon_mcnt %u\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],
|
|
stats.bcn_rcv_cnt, stats.bcn_miss_cnt);
|
|
if (priv->phandle->fw_getlog_enable) {
|
|
sprintf(buf + strlen(buf), "tx_frag_cnt %u\n",
|
|
stats.tx_frag_cnt);
|
|
sprintf(buf + strlen(buf), "qos_tx_frag_cnt ");
|
|
for (i = 0; i < 8; i++) {
|
|
sprintf(buf + strlen(buf), "%u ",
|
|
stats.qos_tx_frag_cnt[i]);
|
|
}
|
|
sprintf(buf + strlen(buf), "\nqos_failed_cnt ");
|
|
for (i = 0; i < 8; i++) {
|
|
sprintf(buf + strlen(buf), "%u ",
|
|
stats.qos_failed_cnt[i]);
|
|
}
|
|
sprintf(buf + strlen(buf), "\nqos_retry_cnt ");
|
|
for (i = 0; i < 8; i++) {
|
|
sprintf(buf + strlen(buf), "%u ",
|
|
stats.qos_retry_cnt[i]);
|
|
}
|
|
sprintf(buf + strlen(buf), "\nqos_multi_retry_cnt ");
|
|
for (i = 0; i < 8; i++) {
|
|
sprintf(buf + strlen(buf), "%u ",
|
|
stats.qos_multi_retry_cnt[i]);
|
|
}
|
|
sprintf(buf + strlen(buf), "\nqos_frm_dup_cnt ");
|
|
for (i = 0; i < 8; i++) {
|
|
sprintf(buf + strlen(buf), "%u ",
|
|
stats.qos_frm_dup_cnt[i]);
|
|
}
|
|
sprintf(buf + strlen(buf), "\nqos_rts_suc_cnt ");
|
|
for (i = 0; i < 8; i++) {
|
|
sprintf(buf + strlen(buf), "%u ",
|
|
stats.qos_rts_suc_cnt[i]);
|
|
}
|
|
sprintf(buf + strlen(buf),
|
|
"\nqos_rts_failure_cnt ");
|
|
for (i = 0; i < 8; i++) {
|
|
sprintf(buf + strlen(buf), "%u ",
|
|
stats.qos_rts_failure_cnt[i]);
|
|
}
|
|
sprintf(buf + strlen(buf), "\nqos_ack_failure_cnt ");
|
|
for (i = 0; i < 8; i++) {
|
|
sprintf(buf + strlen(buf), "%u ",
|
|
stats.qos_ack_failure_cnt[i]);
|
|
}
|
|
sprintf(buf + strlen(buf), "\nqos_rx_frag_cnt ");
|
|
for (i = 0; i < 8; i++) {
|
|
sprintf(buf + strlen(buf), "%u ",
|
|
stats.qos_rx_frag_cnt[i]);
|
|
}
|
|
sprintf(buf + strlen(buf), "\nqos_tx_frm_cnt ");
|
|
for (i = 0; i < 8; i++) {
|
|
sprintf(buf + strlen(buf), "%u ",
|
|
stats.qos_tx_frm_cnt[i]);
|
|
}
|
|
sprintf(buf + strlen(buf), "\nqos_discarded_frm_cnt ");
|
|
for (i = 0; i < 8; i++) {
|
|
sprintf(buf + strlen(buf), "%u ",
|
|
stats.qos_discarded_frm_cnt[i]);
|
|
}
|
|
sprintf(buf + strlen(buf), "\nqos_mpdus_rx_cnt ");
|
|
for (i = 0; i < 8; i++) {
|
|
sprintf(buf + strlen(buf), "%u ",
|
|
stats.qos_mpdus_rx_cnt[i]);
|
|
}
|
|
sprintf(buf + strlen(buf), "\nqos_retries_rx_cnt ");
|
|
for (i = 0; i < 8; i++) {
|
|
sprintf(buf + strlen(buf), "%u ",
|
|
stats.qos_retries_rx_cnt[i]);
|
|
}
|
|
sprintf(buf + strlen(buf),
|
|
"\nmgmt_ccmp_replays %u\n"
|
|
"tx_amsdu_cnt %u\n"
|
|
"failed_amsdu_cnt %u\n"
|
|
"retry_amsdu_cnt %u\n"
|
|
"multi_retry_amsdu_cnt %u\n"
|
|
"tx_octets_in_amsdu_cnt %llu\n"
|
|
"amsdu_ack_failure_cnt %u\n"
|
|
"rx_amsdu_cnt %u\n"
|
|
"rx_octets_in_amsdu_cnt %llu\n"
|
|
"tx_ampdu_cnt %u\n"
|
|
"tx_mpdus_in_ampdu_cnt %u\n"
|
|
"tx_octets_in_ampdu_cnt %llu\n"
|
|
"ampdu_rx_cnt %u\n"
|
|
"mpdu_in_rx_ampdu_cnt %u\n"
|
|
"rx_octets_in_ampdu_cnt %llu\n"
|
|
"ampdu_delimiter_crc_error_cnt %u\n",
|
|
stats.mgmt_ccmp_replays, stats.tx_amsdu_cnt,
|
|
stats.failed_amsdu_cnt, stats.retry_amsdu_cnt,
|
|
stats.multi_retry_amsdu_cnt,
|
|
stats.tx_octets_in_amsdu_cnt,
|
|
stats.amsdu_ack_failure_cnt, stats.rx_amsdu_cnt,
|
|
stats.rx_octets_in_amsdu_cnt,
|
|
stats.tx_ampdu_cnt, stats.tx_mpdus_in_ampdu_cnt,
|
|
stats.tx_octets_in_ampdu_cnt,
|
|
stats.ampdu_rx_cnt, stats.mpdu_in_rx_ampdu_cnt,
|
|
stats.rx_octets_in_ampdu_cnt,
|
|
stats.ampdu_delimiter_crc_error_cnt);
|
|
}
|
|
wrq->u.data.length = MIN(GETLOG_BUFSIZE - 1, 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:
|
|
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,
|
|
DEF_DEAUTH_REASON_CODE)) {
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
} else {
|
|
if (MLAN_STATUS_SUCCESS !=
|
|
woal_disconnect(priv, MOAL_IOCTL_WAIT, NULL,
|
|
DEF_DEAUTH_REASON_CODE))
|
|
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, copy_len;
|
|
int ret = 0;
|
|
mlan_bss_info bss_info;
|
|
mlan_ds_power_cfg *pcfg = NULL;
|
|
mlan_ioctl_req *req = NULL;
|
|
int power_data[MAX_POWER_TABLE_SIZE];
|
|
int i, power_ext_len = 0;
|
|
int *ptr = power_data;
|
|
mlan_power_group *pwr_grp = NULL;
|
|
mlan_status status = MLAN_STATUS_SUCCESS;
|
|
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;
|
|
copy_len = MIN(sizeof(data), sizeof(int) * user_data_len);
|
|
|
|
if (user_data_len) {
|
|
if (sizeof(int) * user_data_len > sizeof(data)) {
|
|
PRINTM(MERROR, "Too many arguments\n");
|
|
ret = -EINVAL;
|
|
goto done;
|
|
}
|
|
if (copy_from_user(data, wrq->u.data.pointer, copy_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;
|
|
if (data[0] == 0xFF)
|
|
pcfg->param.power_ext.power_group[0].rate_format =
|
|
TX_PWR_CFG_AUTO_CTRL_OFF;
|
|
else {
|
|
pcfg->param.power_ext.power_group[0].power_step = 0;
|
|
pcfg->param.power_ext.power_group[0].first_rate_ind =
|
|
data[0];
|
|
pcfg->param.power_ext.power_group[0].last_rate_ind =
|
|
data[0];
|
|
if (data[0] <= 11) {
|
|
pcfg->param.power_ext.power_group[0]
|
|
.rate_format = MLAN_RATE_FORMAT_LG;
|
|
pcfg->param.power_ext.power_group[0].bandwidth =
|
|
MLAN_HT_BW20;
|
|
} else if (data[0] <= 27) {
|
|
pcfg->param.power_ext.power_group[0]
|
|
.rate_format = MLAN_RATE_FORMAT_HT;
|
|
pcfg->param.power_ext.power_group[0].bandwidth =
|
|
MLAN_HT_BW20;
|
|
pcfg->param.power_ext.power_group[0]
|
|
.first_rate_ind -= 12;
|
|
pcfg->param.power_ext.power_group[0]
|
|
.last_rate_ind -= 12;
|
|
} else if ((140 <= data[0]) && (data[0] <= 155)) {
|
|
pcfg->param.power_ext.power_group[0]
|
|
.rate_format = MLAN_RATE_FORMAT_HT;
|
|
pcfg->param.power_ext.power_group[0].bandwidth =
|
|
MLAN_HT_BW40;
|
|
pcfg->param.power_ext.power_group[0]
|
|
.first_rate_ind -= 140;
|
|
pcfg->param.power_ext.power_group[0]
|
|
.last_rate_ind -= 140;
|
|
}
|
|
if (user_data_len == 2) {
|
|
pcfg->param.power_ext.power_group[0].power_min =
|
|
data[1];
|
|
pcfg->param.power_ext.power_group[0].power_max =
|
|
data[1];
|
|
} else if (user_data_len == 4) {
|
|
pcfg->param.power_ext.power_group[0].power_min =
|
|
data[1];
|
|
pcfg->param.power_ext.power_group[0].power_max =
|
|
data[2];
|
|
pcfg->param.power_ext.power_group[0].power_step =
|
|
data[3];
|
|
}
|
|
pcfg->param.power_ext.num_pwr_grp = 1;
|
|
}
|
|
}
|
|
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
|
|
if (status != MLAN_STATUS_SUCCESS) {
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
if (!user_data_len) {
|
|
/* GET operation */
|
|
i = 0;
|
|
power_ext_len = 0;
|
|
ptr = power_data;
|
|
while ((i < (int)pcfg->param.power_ext.num_pwr_grp) &&
|
|
((power_ext_len + 5) < MAX_POWER_TABLE_SIZE)) {
|
|
pwr_grp = &pcfg->param.power_ext.power_group[i];
|
|
if (pwr_grp->rate_format == MLAN_RATE_FORMAT_HT) {
|
|
if (pwr_grp->bandwidth == MLAN_HT_BW20) {
|
|
pwr_grp->first_rate_ind += 12;
|
|
pwr_grp->last_rate_ind += 12;
|
|
} else if (pwr_grp->bandwidth == MLAN_HT_BW40) {
|
|
pwr_grp->first_rate_ind += 140;
|
|
pwr_grp->last_rate_ind += 140;
|
|
}
|
|
}
|
|
|
|
if ((pwr_grp->rate_format == MLAN_RATE_FORMAT_LG) ||
|
|
(pwr_grp->rate_format == MLAN_RATE_FORMAT_HT)) {
|
|
*ptr = pwr_grp->first_rate_ind;
|
|
ptr++;
|
|
*ptr = pwr_grp->last_rate_ind;
|
|
ptr++;
|
|
*ptr = pwr_grp->power_min;
|
|
ptr++;
|
|
*ptr = pwr_grp->power_max;
|
|
ptr++;
|
|
*ptr = pwr_grp->power_step;
|
|
ptr++;
|
|
power_ext_len += 5;
|
|
}
|
|
i++;
|
|
}
|
|
if (copy_to_user(wrq->u.data.pointer, (t_u8 *)power_data,
|
|
sizeof(int) * power_ext_len)) {
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
wrq->u.data.length = power_ext_len;
|
|
}
|
|
done:
|
|
if (status != MLAN_STATUS_PENDING)
|
|
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;
|
|
mlan_status status = MLAN_STATUS_SUCCESS;
|
|
|
|
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;
|
|
|
|
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
|
|
if (status != MLAN_STATUS_SUCCESS) {
|
|
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 (status != MLAN_STATUS_PENDING)
|
|
kfree(req);
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
#ifdef SDIO
|
|
/**
|
|
* @brief Turn on/off the sdio clock
|
|
*
|
|
* @param priv A pointer to moal_private structure
|
|
* @param wrq A pointer to iwreq structure
|
|
*
|
|
* @return 0/MLAN_STATUS_SUCCESS --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:
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
#endif /* SDIO */
|
|
|
|
/**
|
|
* @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;
|
|
mlan_status status = MLAN_STATUS_SUCCESS;
|
|
|
|
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;
|
|
}
|
|
|
|
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
|
|
if (status != MLAN_STATUS_SUCCESS) {
|
|
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 (status != MLAN_STATUS_PENDING)
|
|
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;
|
|
mlan_status status = MLAN_STATUS_SUCCESS;
|
|
|
|
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_MCS15))) {
|
|
ret = -EINVAL;
|
|
goto done;
|
|
}
|
|
}
|
|
rate->param.rate_cfg.rate = rateindex;
|
|
}
|
|
|
|
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
|
|
if (status != MLAN_STATUS_SUCCESS) {
|
|
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 (status != MLAN_STATUS_PENDING)
|
|
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;
|
|
mlan_status status = MLAN_STATUS_SUCCESS;
|
|
|
|
ENTER();
|
|
|
|
if (wrq->u.data.length) {
|
|
if (copy_from_user(®ion, 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;
|
|
}
|
|
|
|
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
|
|
if (status != MLAN_STATUS_SUCCESS) {
|
|
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 (status != MLAN_STATUS_PENDING)
|
|
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], copy_len;
|
|
int ret = 0;
|
|
int data_length = wrq->u.data.length;
|
|
ENTER();
|
|
|
|
copy_len = MIN(sizeof(data), sizeof(int) * data_length);
|
|
if (!data_length) {
|
|
data[0] = drvdbg;
|
|
/* Return the current driver debug bit masks */
|
|
if (copy_to_user(wrq->u.data.pointer, data, sizeof(int))) {
|
|
PRINTM(MERROR, "Copy to user failed\n");
|
|
ret = -EFAULT;
|
|
goto drvdbgexit;
|
|
}
|
|
wrq->u.data.length = 1;
|
|
} else if (data_length < 3) {
|
|
/* Get the driver debug bit masks from user */
|
|
if (copy_from_user(data, wrq->u.data.pointer, copy_len)) {
|
|
PRINTM(MERROR, "Copy from user failed\n");
|
|
ret = -EFAULT;
|
|
goto drvdbgexit;
|
|
}
|
|
drvdbg = data[0];
|
|
/* Set the driver debug bit masks into mlan */
|
|
woal_set_drvdbg(priv, drvdbg);
|
|
} else {
|
|
PRINTM(MERROR, "Invalid parameter number\n");
|
|
goto drvdbgexit;
|
|
}
|
|
|
|
printk(KERN_ALERT "drvdbg = 0x%08x\n", drvdbg);
|
|
#ifdef DEBUG_LEVEL2
|
|
printk(KERN_ALERT "MINFO (%08x) %s\n", MINFO,
|
|
(drvdbg & MINFO) ? "X" : "");
|
|
printk(KERN_ALERT "MWARN (%08x) %s\n", MWARN,
|
|
(drvdbg & MWARN) ? "X" : "");
|
|
printk(KERN_ALERT "MENTRY (%08x) %s\n", MENTRY,
|
|
(drvdbg & MENTRY) ? "X" : "");
|
|
#endif
|
|
printk(KERN_ALERT "MMPA_D (%08x) %s\n", MMPA_D,
|
|
(drvdbg & MMPA_D) ? "X" : "");
|
|
printk(KERN_ALERT "MIF_D (%08x) %s\n", MIF_D,
|
|
(drvdbg & MIF_D) ? "X" : "");
|
|
printk(KERN_ALERT "MFW_D (%08x) %s\n", MFW_D,
|
|
(drvdbg & MFW_D) ? "X" : "");
|
|
printk(KERN_ALERT "MEVT_D (%08x) %s\n", MEVT_D,
|
|
(drvdbg & MEVT_D) ? "X" : "");
|
|
printk(KERN_ALERT "MCMD_D (%08x) %s\n", MCMD_D,
|
|
(drvdbg & MCMD_D) ? "X" : "");
|
|
printk(KERN_ALERT "MDAT_D (%08x) %s\n", MDAT_D,
|
|
(drvdbg & MDAT_D) ? "X" : "");
|
|
printk(KERN_ALERT "MREG_D (%08x) %s\n", MREG_D,
|
|
(drvdbg & MREG_D) ? "X" : "");
|
|
printk(KERN_ALERT "MIOCTL (%08x) %s\n", MIOCTL,
|
|
(drvdbg & MIOCTL) ? "X" : "");
|
|
printk(KERN_ALERT "MINTR (%08x) %s\n", MINTR,
|
|
(drvdbg & MINTR) ? "X" : "");
|
|
printk(KERN_ALERT "MEVENT (%08x) %s\n", MEVENT,
|
|
(drvdbg & MEVENT) ? "X" : "");
|
|
printk(KERN_ALERT "MCMND (%08x) %s\n", MCMND,
|
|
(drvdbg & MCMND) ? "X" : "");
|
|
printk(KERN_ALERT "MDATA (%08x) %s\n", MDATA,
|
|
(drvdbg & MDATA) ? "X" : "");
|
|
printk(KERN_ALERT "MERROR (%08x) %s\n", MERROR,
|
|
(drvdbg & MERROR) ? "X" : "");
|
|
printk(KERN_ALERT "MFATAL (%08x) %s\n", MFATAL,
|
|
(drvdbg & MFATAL) ? "X" : "");
|
|
printk(KERN_ALERT "MMSG (%08x) %s\n", MMSG,
|
|
(drvdbg & MMSG) ? "X" : "");
|
|
|
|
drvdbgexit:
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
#endif /* DEBUG_LEVEL1 */
|
|
|
|
/**
|
|
* @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;
|
|
mlan_status status = MLAN_STATUS_SUCCESS;
|
|
|
|
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;
|
|
|
|
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
|
|
if (status != MLAN_STATUS_SUCCESS) {
|
|
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 (status != MLAN_STATUS_PENDING)
|
|
kfree(req);
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
#ifndef OPCHAN
|
|
/**
|
|
* @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;
|
|
mlan_status status = MLAN_STATUS_SUCCESS;
|
|
|
|
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;
|
|
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
|
|
if (status != MLAN_STATUS_SUCCESS) {
|
|
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 (status != MLAN_STATUS_PENDING)
|
|
kfree(req);
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* @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;
|
|
mlan_status status = MLAN_STATUS_SUCCESS;
|
|
|
|
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;
|
|
|
|
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
|
|
if (status != MLAN_STATUS_SUCCESS) {
|
|
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 (status != MLAN_STATUS_PENDING)
|
|
kfree(req);
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Configure 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, copy_len;
|
|
int data_length = wrq->u.data.length;
|
|
#ifdef DEBUG_LEVEL1
|
|
char err_str[][36] = {{"sleep clock error in ppm"},
|
|
{"wakeup offset in usec"},
|
|
{"clock stabilization time in usec"},
|
|
{"control periodic calibration(0-2)"},
|
|
{"control of external sleepClock(0-2)"},
|
|
{"value of reserved for debug"}};
|
|
#endif
|
|
mlan_status status = MLAN_STATUS_SUCCESS;
|
|
|
|
ENTER();
|
|
|
|
copy_len = MIN(sizeof(data), sizeof(int) * data_length);
|
|
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 (data_length == 0) {
|
|
req->action = MLAN_ACT_GET;
|
|
} else if (data_length == 6) {
|
|
if (copy_from_user(data, wrq->u.data.pointer, copy_len)) {
|
|
/* copy_from_user failed */
|
|
PRINTM(MERROR, "S_PARAMS: copy from user failed\n");
|
|
ret = -EINVAL;
|
|
goto done;
|
|
}
|
|
|
|
#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;
|
|
}
|
|
|
|
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
|
|
if (status != MLAN_STATUS_SUCCESS) {
|
|
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 (status != MLAN_STATUS_PENDING)
|
|
kfree(req);
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Set/get user provisioned local power constraint
|
|
*
|
|
* @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_11h_local_pwr_constraint(moal_private *priv,
|
|
struct iwreq *wrq)
|
|
{
|
|
int ret = 0, data = 0;
|
|
mlan_ioctl_req *req = NULL;
|
|
mlan_ds_11h_cfg *ds_11hcfg = NULL;
|
|
mlan_status status = MLAN_STATUS_SUCCESS;
|
|
|
|
ENTER();
|
|
|
|
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11h_cfg));
|
|
if (req == NULL) {
|
|
ret = -ENOMEM;
|
|
goto done;
|
|
}
|
|
ds_11hcfg = (mlan_ds_11h_cfg *)req->pbuf;
|
|
if (wrq->u.data.length) {
|
|
if (copy_from_user(&data, wrq->u.data.pointer, sizeof(int))) {
|
|
PRINTM(MINFO, "Copy from user failed\n");
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
ds_11hcfg->param.usr_local_power_constraint = (t_s8)data;
|
|
req->action = MLAN_ACT_SET;
|
|
} else
|
|
req->action = MLAN_ACT_GET;
|
|
|
|
ds_11hcfg->sub_command = MLAN_OID_11H_LOCAL_POWER_CONSTRAINT;
|
|
req->req_id = MLAN_IOCTL_11H_CFG;
|
|
|
|
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
|
|
if (status != MLAN_STATUS_SUCCESS) {
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
|
|
/* Copy response to user */
|
|
if (req->action == MLAN_ACT_GET) {
|
|
data = (int)ds_11hcfg->param.usr_local_power_constraint;
|
|
if (copy_to_user(wrq->u.data.pointer, &data, sizeof(int))) {
|
|
PRINTM(MINFO, "Copy to user failed\n");
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
wrq->u.data.length = 1;
|
|
}
|
|
|
|
done:
|
|
if (status != MLAN_STATUS_PENDING)
|
|
kfree(req);
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Set/get HT stream configurations
|
|
*
|
|
* @param priv A pointer to moal_private structure
|
|
* @param wrq A pointer to iwreq structure
|
|
* @return 0 --success, otherwise fail
|
|
*/
|
|
static int woal_ht_stream_cfg_ioctl(moal_private *priv, struct iwreq *wrq)
|
|
{
|
|
int ret = 0, data = 0;
|
|
mlan_ioctl_req *req = NULL;
|
|
mlan_ds_11n_cfg *cfg = NULL;
|
|
mlan_status status = MLAN_STATUS_SUCCESS;
|
|
|
|
ENTER();
|
|
|
|
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg));
|
|
if (req == NULL) {
|
|
ret = -ENOMEM;
|
|
goto done;
|
|
}
|
|
cfg = (mlan_ds_11n_cfg *)req->pbuf;
|
|
if (wrq->u.data.length) {
|
|
if (copy_from_user(&data, wrq->u.data.pointer, sizeof(int))) {
|
|
PRINTM(MINFO, "Copy from user failed\n");
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
if (data != HT_STREAM_MODE_1X1 && data != HT_STREAM_MODE_2X2) {
|
|
PRINTM(MINFO, "Invalid argument\n");
|
|
ret = -EINVAL;
|
|
goto done;
|
|
}
|
|
cfg->param.stream_cfg = data;
|
|
req->action = MLAN_ACT_SET;
|
|
} else
|
|
req->action = MLAN_ACT_GET;
|
|
|
|
cfg->sub_command = MLAN_OID_11N_CFG_STREAM_CFG;
|
|
req->req_id = MLAN_IOCTL_11N_CFG;
|
|
|
|
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
|
|
if (status != MLAN_STATUS_SUCCESS) {
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
|
|
/* Copy response to user */
|
|
data = (int)cfg->param.stream_cfg;
|
|
if (copy_to_user(wrq->u.data.pointer, &data, sizeof(int))) {
|
|
PRINTM(MINFO, "Copy to user failed\n");
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
wrq->u.data.length = 1;
|
|
|
|
done:
|
|
if (status != MLAN_STATUS_PENDING)
|
|
kfree(req);
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Set/get MAC control configuration
|
|
*
|
|
* @param priv A pointer to moal_private structure
|
|
* @param wrq A pointer to iwreq structure
|
|
* @return 0 --success, otherwise fail
|
|
*/
|
|
static int woal_mac_control_ioctl(moal_private *priv, struct iwreq *wrq)
|
|
{
|
|
int ret = 0, data = 0;
|
|
mlan_ioctl_req *req = NULL;
|
|
mlan_ds_misc_cfg *cfg = NULL;
|
|
mlan_status status = MLAN_STATUS_SUCCESS;
|
|
|
|
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;
|
|
if (wrq->u.data.length) {
|
|
if (copy_from_user(&data, wrq->u.data.pointer, sizeof(int))) {
|
|
PRINTM(MINFO, "Copy from user failed\n");
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
/* Validation will be done later */
|
|
cfg->param.mac_ctrl = data;
|
|
req->action = MLAN_ACT_SET;
|
|
} else
|
|
req->action = MLAN_ACT_GET;
|
|
|
|
cfg->sub_command = MLAN_OID_MISC_MAC_CONTROL;
|
|
req->req_id = MLAN_IOCTL_MISC_CFG;
|
|
|
|
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
|
|
if (status != MLAN_STATUS_SUCCESS) {
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
|
|
/* Copy response to user */
|
|
data = (int)cfg->param.mac_ctrl;
|
|
if (copy_to_user(wrq->u.data.pointer, &data, sizeof(int))) {
|
|
PRINTM(MINFO, "Copy to user failed\n");
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
wrq->u.data.length = 1;
|
|
|
|
done:
|
|
if (status != MLAN_STATUS_PENDING)
|
|
kfree(req);
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Get thermal reading
|
|
*
|
|
* @param priv A pointer to moal_private structure
|
|
* @param wrq A pointer to iwreq structure
|
|
* @return 0 --success, otherwise fail
|
|
*/
|
|
static int woal_thermal_ioctl(moal_private *priv, struct iwreq *wrq)
|
|
{
|
|
int ret = 0, data = 0;
|
|
mlan_ioctl_req *req = NULL;
|
|
mlan_ds_misc_cfg *cfg = NULL;
|
|
mlan_status status = MLAN_STATUS_SUCCESS;
|
|
|
|
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;
|
|
if (wrq->u.data.length) {
|
|
PRINTM(MERROR, "Set is not supported for this command\n");
|
|
ret = -EINVAL;
|
|
goto done;
|
|
}
|
|
req->action = MLAN_ACT_GET;
|
|
|
|
cfg->sub_command = MLAN_OID_MISC_THERMAL;
|
|
req->req_id = MLAN_IOCTL_MISC_CFG;
|
|
|
|
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
|
|
if (status != MLAN_STATUS_SUCCESS) {
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
|
|
/* Copy response to user */
|
|
data = (int)cfg->param.thermal;
|
|
if (copy_to_user(wrq->u.data.pointer, &data, sizeof(int))) {
|
|
PRINTM(MINFO, "Copy to user failed\n");
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
wrq->u.data.length = 1;
|
|
|
|
done:
|
|
if (status != MLAN_STATUS_PENDING)
|
|
kfree(req);
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Set/get hotspot enable state
|
|
*
|
|
* @param priv Pointer to the moal_private driver data struct
|
|
* @param wrq Pointer to user data
|
|
*
|
|
* @return 0 --success, otherwise fail
|
|
*/
|
|
static int woal_cfg_hotspot(moal_private *priv, struct iwreq *wrq)
|
|
{
|
|
int ret = 0;
|
|
mlan_ioctl_req *req = NULL;
|
|
mlan_ds_misc_cfg *cfg = NULL;
|
|
int config;
|
|
mlan_status status = MLAN_STATUS_SUCCESS;
|
|
|
|
ENTER();
|
|
|
|
if (wrq->u.data.length > 1) {
|
|
PRINTM(MERROR, "Invalid no 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;
|
|
}
|
|
cfg = (mlan_ds_misc_cfg *)req->pbuf;
|
|
if (wrq->u.data.length == 0)
|
|
req->action = MLAN_ACT_GET;
|
|
else {
|
|
if (copy_from_user(&config, wrq->u.data.pointer, sizeof(int))) {
|
|
PRINTM(MERROR, "copy from user failed\n");
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
cfg->param.hotspot_cfg = config;
|
|
req->action = MLAN_ACT_SET;
|
|
}
|
|
|
|
cfg->sub_command = MLAN_OID_MISC_HOTSPOT_CFG;
|
|
req->req_id = MLAN_IOCTL_MISC_CFG;
|
|
|
|
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
|
|
if (status != MLAN_STATUS_SUCCESS) {
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
|
|
config = cfg->param.hotspot_cfg;
|
|
if (copy_to_user(wrq->u.data.pointer, &config, sizeof(int))) {
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
wrq->u.data.length = 1;
|
|
|
|
done:
|
|
if (status != MLAN_STATUS_PENDING)
|
|
kfree(req);
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
#if defined(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 &= ~MBIT(priv->bss_index);
|
|
priv->reassoc_on = MFALSE;
|
|
priv->reassoc_required = MFALSE;
|
|
if (!handle->reassoc_on &&
|
|
handle->is_reassoc_timer_set == MTRUE) {
|
|
woal_cancel_timer(&handle->reassoc_timer);
|
|
handle->is_reassoc_timer_set = MFALSE;
|
|
}
|
|
} else {
|
|
handle->reassoc_on |= MBIT(priv->bss_index);
|
|
priv->reassoc_on = MTRUE;
|
|
}
|
|
} else {
|
|
data = (int)(priv->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;
|
|
mlan_status status = MLAN_STATUS_SUCCESS;
|
|
|
|
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;
|
|
}
|
|
|
|
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
|
|
if (status != MLAN_STATUS_SUCCESS) {
|
|
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 (status != MLAN_STATUS_PENDING)
|
|
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;
|
|
mlan_status status = MLAN_STATUS_SUCCESS;
|
|
|
|
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;
|
|
}
|
|
|
|
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
|
|
if (status != MLAN_STATUS_SUCCESS) {
|
|
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 (status != MLAN_STATUS_PENDING)
|
|
kfree(req);
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Implement 802.11D clear chan table 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_clr_chan_table(moal_private *priv, struct iwreq *wrq)
|
|
{
|
|
int ret = 0;
|
|
mlan_ds_11d_cfg *pcfg_11d = NULL;
|
|
mlan_ioctl_req *req = NULL;
|
|
mlan_status status = MLAN_STATUS_SUCCESS;
|
|
|
|
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_CLR_CHAN_TABLE;
|
|
req->action = MLAN_ACT_SET;
|
|
|
|
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
|
|
if (status != MLAN_STATUS_SUCCESS) {
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
|
|
done:
|
|
if (status != MLAN_STATUS_PENDING)
|
|
kfree(req);
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief 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;
|
|
mlan_status status = MLAN_STATUS_SUCCESS;
|
|
|
|
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;
|
|
|
|
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
|
|
if (status != MLAN_STATUS_SUCCESS) {
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
|
|
done:
|
|
if (status != MLAN_STATUS_PENDING)
|
|
kfree(req);
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief 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;
|
|
char buf[256];
|
|
char *begin = NULL, *end = NULL, *opt = NULL;
|
|
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;
|
|
int data_length = wrq->u.data.length, copy_len;
|
|
mlan_status status = MLAN_STATUS_SUCCESS;
|
|
|
|
ENTER();
|
|
|
|
if (!priv->phandle->card_info->embedded_supp) {
|
|
PRINTM(MERROR, "Not supported cmd on this card\n");
|
|
ret = -EOPNOTSUPP;
|
|
goto done;
|
|
}
|
|
if (!data_length || data_length >= (int)sizeof(buf) - 1) {
|
|
PRINTM(MERROR,
|
|
"Argument missing or too long for setpassphrase\n");
|
|
ret = -EINVAL;
|
|
goto done;
|
|
}
|
|
memset(buf, 0, sizeof(buf));
|
|
copy_len = data_length;
|
|
|
|
if (copy_from_user(buf, wrq->u.data.pointer, copy_len)) {
|
|
PRINTM(MERROR, "Copy from user failed\n");
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
buf[copy_len] = '\0';
|
|
|
|
/* Parse the buf to get the cmd_action */
|
|
begin = buf;
|
|
end = woal_strsep(&begin, ';', '/');
|
|
if (!end) {
|
|
PRINTM(MERROR, "Invalid option\n");
|
|
ret = -EINVAL;
|
|
goto done;
|
|
}
|
|
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);
|
|
moal_memcpy_ext(priv->phandle,
|
|
sec->param.passphrase.ssid.ssid, end,
|
|
strlen(end), MLAN_MAX_SSID_LENGTH);
|
|
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(sec->param.passphrase.bssid, end);
|
|
} else if (!strnicmp(opt, "psk", strlen(opt)) &&
|
|
req->action == MLAN_ACT_SET) {
|
|
if (strlen(end) != MLAN_PMK_HEXSTR_LENGTH) {
|
|
PRINTM(MERROR, "Invalid PMK length\n");
|
|
ret = -EINVAL;
|
|
break;
|
|
}
|
|
woal_ascii2hex(
|
|
(t_u8 *)(sec->param.passphrase.psk.pmk.pmk),
|
|
end, MLAN_PMK_HEXSTR_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;
|
|
moal_memcpy_ext(
|
|
priv->phandle,
|
|
sec->param.passphrase.psk.passphrase.passphrase,
|
|
end,
|
|
sizeof(sec->param.passphrase.psk.passphrase
|
|
.passphrase),
|
|
sizeof(sec->param.passphrase.psk.passphrase
|
|
.passphrase));
|
|
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;
|
|
else if (action == 0)
|
|
sec->param.passphrase.psk_type = MLAN_PSK_QUERY;
|
|
|
|
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
|
|
if (status != MLAN_STATUS_SUCCESS) {
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
if (action == 0) {
|
|
memset(buf, 0, sizeof(buf));
|
|
if (sec->param.passphrase.ssid.ssid_len) {
|
|
len += sprintf(buf + len, "ssid:");
|
|
moal_memcpy_ext(priv->phandle, buf + len,
|
|
sec->param.passphrase.ssid.ssid,
|
|
sec->param.passphrase.ssid.ssid_len,
|
|
sizeof(buf) - 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_KEY_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,
|
|
MIN(len, sizeof(buf)))) {
|
|
PRINTM(MERROR, "Copy to user failed, len %d\n",
|
|
len);
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
wrq->u.data.length = len;
|
|
}
|
|
}
|
|
done:
|
|
if (status != MLAN_STATUS_PENDING)
|
|
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;
|
|
mlan_status status = MLAN_STATUS_SUCCESS;
|
|
|
|
ENTER();
|
|
|
|
if (!priv->phandle->card_info->embedded_supp) {
|
|
PRINTM(MERROR, "Not supported cmd on this card\n");
|
|
ret = -EOPNOTSUPP;
|
|
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_ESUPP_MODE;
|
|
req->req_id = MLAN_IOCTL_SEC_CFG;
|
|
req->action = MLAN_ACT_GET;
|
|
|
|
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
|
|
if (status != MLAN_STATUS_SUCCESS) {
|
|
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 (status != MLAN_STATUS_PENDING)
|
|
kfree(req);
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Get GTK/PTK
|
|
*
|
|
* @param priv A pointer to moal_private structure
|
|
* @param wrq A pointer to user data
|
|
*
|
|
* @return 0 --success, otherwise fail
|
|
*/
|
|
static int woal_get_key_ioctl(moal_private *priv, struct iwreq *wrq)
|
|
{
|
|
int ret = 0;
|
|
unsigned int i;
|
|
t_u8 key_ascii[256];
|
|
t_u8 *tmp;
|
|
mlan_ds_sec_cfg *sec = NULL;
|
|
mlan_ioctl_req *req = NULL;
|
|
mlan_status status = MLAN_STATUS_SUCCESS;
|
|
|
|
ENTER();
|
|
|
|
memset(key_ascii, 0x00, sizeof(key_ascii));
|
|
tmp = key_ascii;
|
|
|
|
if (priv->media_connected == MFALSE) {
|
|
PRINTM(MERROR, "Can't get key in un-associated state\n");
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
|
|
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg));
|
|
if (req == NULL) {
|
|
ret = -ENOMEM;
|
|
goto done;
|
|
}
|
|
|
|
/* Get Unicast 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_QUERY_KEY;
|
|
sec->param.encrypt_key.key_index = 0;
|
|
sec->param.encrypt_key.key_flags = 0;
|
|
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
|
|
if (status != MLAN_STATUS_SUCCESS) {
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
if (sec->param.encrypt_key.key_len) {
|
|
sprintf((char *)tmp, "\n%s", "PTK: ");
|
|
tmp += 5;
|
|
for (i = 0; i < sec->param.encrypt_key.key_len; i++)
|
|
tmp += sprintf((char *)tmp, "%02x",
|
|
sec->param.encrypt_key.key_material[i]);
|
|
}
|
|
|
|
/* Get Multicase 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_QUERY_KEY;
|
|
sec->param.encrypt_key.key_index = 0;
|
|
sec->param.encrypt_key.key_flags = KEY_FLAG_GROUP_KEY;
|
|
memset(sec->param.encrypt_key.mac_addr, 0x0, MLAN_MAC_ADDR_LENGTH);
|
|
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
|
|
if (status != MLAN_STATUS_SUCCESS) {
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
if (sec->param.encrypt_key.key_len) {
|
|
sprintf((char *)tmp, "\n%s", "GTK: ");
|
|
tmp += 5;
|
|
for (i = 0; i < sec->param.encrypt_key.key_len; i++)
|
|
tmp += sprintf((char *)tmp, "%02x",
|
|
sec->param.encrypt_key.key_material[i]);
|
|
}
|
|
|
|
/* Get IGTK 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_QUERY_KEY;
|
|
sec->param.encrypt_key.key_index = 0;
|
|
sec->param.encrypt_key.key_flags = KEY_FLAG_AES_MCAST_IGTK;
|
|
memset(sec->param.encrypt_key.mac_addr, 0x0, MLAN_MAC_ADDR_LENGTH);
|
|
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
|
|
if (status != MLAN_STATUS_SUCCESS) {
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
if (sec->param.encrypt_key.key_len) {
|
|
sprintf((char *)tmp, "\n%s", "IGTK: ");
|
|
tmp += 6;
|
|
for (i = 0; i < sec->param.encrypt_key.key_len; i++)
|
|
tmp += sprintf((char *)tmp, "%02x",
|
|
sec->param.encrypt_key.key_material[i]);
|
|
}
|
|
|
|
wrq->u.data.length = sizeof(key_ascii) + 1;
|
|
if (wrq->u.data.pointer) {
|
|
if (copy_to_user(wrq->u.data.pointer, &key_ascii,
|
|
tmp - key_ascii)) {
|
|
PRINTM(MERROR, "copy_to_user failed\n");
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
}
|
|
done:
|
|
if (status != MLAN_STATUS_PENDING)
|
|
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;
|
|
int data_length = wrq->u.data.length, copy_len;
|
|
mlan_status status = MLAN_STATUS_SUCCESS;
|
|
|
|
ENTER();
|
|
|
|
copy_len = MIN(sizeof(misc->param.gen_ie.ie_data),
|
|
sizeof(int) * data_length);
|
|
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 = data_length;
|
|
|
|
/* get the whole command from user */
|
|
if (copy_from_user(misc->param.gen_ie.ie_data, wrq->u.data.pointer,
|
|
copy_len)) {
|
|
PRINTM(MERROR, "copy from user failed\n");
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
|
|
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
|
|
if (status != MLAN_STATUS_SUCCESS) {
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
done:
|
|
if (status != MLAN_STATUS_PENDING)
|
|
kfree(req);
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Set/get IP address
|
|
*
|
|
* @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_ip_addr(moal_private *priv, struct iwreq *wrq)
|
|
{
|
|
char buf[IPADDR_MAX_BUF];
|
|
mlan_ioctl_req *ioctl_req = NULL;
|
|
mlan_ds_misc_cfg *misc = NULL;
|
|
int ret = 0, op_code = 0, data_length = wrq->u.data.length;
|
|
mlan_status status = MLAN_STATUS_SUCCESS;
|
|
|
|
ENTER();
|
|
|
|
memset(buf, 0, IPADDR_MAX_BUF);
|
|
ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg));
|
|
if (ioctl_req == NULL) {
|
|
ret = -ENOMEM;
|
|
goto done;
|
|
}
|
|
misc = (mlan_ds_misc_cfg *)ioctl_req->pbuf;
|
|
|
|
if (data_length <= 1) { /* GET */
|
|
ioctl_req->action = MLAN_ACT_GET;
|
|
} else {
|
|
if (copy_from_user(buf, wrq->u.data.pointer,
|
|
MIN(IPADDR_MAX_BUF - 1, data_length))) {
|
|
PRINTM(MERROR, "Copy from user failed\n");
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
/* Make sure we have the operation argument */
|
|
if (data_length > 2 && buf[1] != ';') {
|
|
PRINTM(MERROR,
|
|
"No operation argument. Separate with ';'\n");
|
|
ret = -EINVAL;
|
|
goto done;
|
|
} else {
|
|
buf[1] = '\0';
|
|
}
|
|
ioctl_req->action = MLAN_ACT_SET;
|
|
/* only one IP is supported in current firmware */
|
|
memset(misc->param.ipaddr_cfg.ip_addr[0], 0, IPADDR_LEN);
|
|
in4_pton(&buf[2], MIN((IPADDR_MAX_BUF - 3), (data_length - 2)),
|
|
misc->param.ipaddr_cfg.ip_addr[0], ' ', NULL);
|
|
/* only one IP is supported in current firmware */
|
|
misc->param.ipaddr_cfg.ip_addr_num = 1;
|
|
misc->param.ipaddr_cfg.ip_addr_type = IPADDR_TYPE_IPV4;
|
|
}
|
|
if (woal_atoi(&op_code, buf) != MLAN_STATUS_SUCCESS) {
|
|
ret = -EINVAL;
|
|
goto done;
|
|
}
|
|
misc->param.ipaddr_cfg.op_code = (t_u32)op_code;
|
|
ioctl_req->req_id = MLAN_IOCTL_MISC_CFG;
|
|
misc->sub_command = MLAN_OID_MISC_IP_ADDR;
|
|
|
|
/* Send ioctl to mlan */
|
|
status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT);
|
|
if (status != MLAN_STATUS_SUCCESS) {
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
|
|
if (ioctl_req->action == MLAN_ACT_GET) {
|
|
snprintf(buf, IPADDR_MAX_BUF, "%d;%d.%d.%d.%d",
|
|
misc->param.ipaddr_cfg.op_code,
|
|
misc->param.ipaddr_cfg.ip_addr[0][0],
|
|
misc->param.ipaddr_cfg.ip_addr[0][1],
|
|
misc->param.ipaddr_cfg.ip_addr[0][2],
|
|
misc->param.ipaddr_cfg.ip_addr[0][3]);
|
|
wrq->u.data.length = IPADDR_MAX_BUF;
|
|
if (copy_to_user(wrq->u.data.pointer, buf, IPADDR_MAX_BUF)) {
|
|
PRINTM(MERROR, "Copy to user failed\n");
|
|
ret = -EFAULT;
|
|
}
|
|
}
|
|
|
|
done:
|
|
if (status != MLAN_STATUS_PENDING)
|
|
kfree(ioctl_req);
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Set/Get Transmit beamforming capabilities
|
|
*
|
|
* @param priv A pointer to moal_private structure
|
|
* @param wrq A pointer to iwreq structure
|
|
*
|
|
* @return 0 -- success, otherwise fail
|
|
*/
|
|
static int woal_tx_bf_cap_ioctl(moal_private *priv, struct iwreq *wrq)
|
|
{
|
|
int ret = 0, data_length = wrq->u.data.length;
|
|
mlan_ioctl_req *req = NULL;
|
|
mlan_ds_11n_cfg *bf_cfg = NULL;
|
|
int bf_cap = 0;
|
|
mlan_status status = MLAN_STATUS_SUCCESS;
|
|
|
|
ENTER();
|
|
|
|
if (data_length > 1) {
|
|
PRINTM(MERROR, "Invalid no of arguments!\n");
|
|
ret = -EINVAL;
|
|
goto done;
|
|
}
|
|
/* Allocate an IOCTL request buffer */
|
|
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg));
|
|
if (req == NULL) {
|
|
ret = -ENOMEM;
|
|
goto done;
|
|
}
|
|
|
|
/* Fill request buffer */
|
|
bf_cfg = (mlan_ds_11n_cfg *)req->pbuf;
|
|
req->req_id = MLAN_IOCTL_11N_CFG;
|
|
bf_cfg->sub_command = MLAN_OID_11N_CFG_TX_BF_CAP;
|
|
req->action = MLAN_ACT_GET;
|
|
if (data_length) { /* SET */
|
|
if (copy_from_user(&bf_cap, wrq->u.data.pointer, sizeof(int))) {
|
|
PRINTM(MERROR, "copy from user failed\n");
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
bf_cfg->param.tx_bf_cap = bf_cap;
|
|
req->action = MLAN_ACT_SET;
|
|
}
|
|
|
|
/* Send IOCTL request to MLAN */
|
|
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
|
|
if (status != MLAN_STATUS_SUCCESS) {
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
|
|
bf_cap = bf_cfg->param.tx_bf_cap;
|
|
if (copy_to_user(wrq->u.data.pointer, &bf_cap, sizeof(int))) {
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
wrq->u.data.length = 1;
|
|
|
|
done:
|
|
if (status != MLAN_STATUS_PENDING)
|
|
kfree(req);
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
/* Maximum input output characters in group WOAL_SET_GET_256_CHAR */
|
|
#define MAX_IN_OUT_CHAR 256
|
|
/** Tx BF Global conf argument index */
|
|
#define BF_ENABLE_PARAM 1
|
|
#define SOUND_ENABLE_PARAM 2
|
|
#define FB_TYPE_PARAM 3
|
|
#define SNR_THRESHOLD_PARAM 4
|
|
#define SOUND_INTVL_PARAM 5
|
|
#define BF_MODE_PARAM 6
|
|
#define BF_CFG_ACT_GET 0
|
|
#define BF_CFG_ACT_SET 1
|
|
|
|
/**
|
|
* @brief Set/Get Transmit beamforming configuration
|
|
*
|
|
* @param priv A pointer to moal_private structure
|
|
* @param wrq A pointer to iwreq structure
|
|
*
|
|
* @return 0 -- success, otherwise fail
|
|
*/
|
|
static int woal_tx_bf_cfg_ioctl(moal_private *priv, struct iwreq *wrq)
|
|
{
|
|
int ret = 0, data_length = wrq->u.data.length;
|
|
int bf_action = 0, interval = 0;
|
|
int snr = 0, i, tmp_val = 0;
|
|
t_u8 buf[MAX_IN_OUT_CHAR], char_count = 0;
|
|
t_u8 *str, *token, *pos;
|
|
t_u16 action = 0;
|
|
|
|
mlan_ds_11n_tx_bf_cfg bf_cfg;
|
|
mlan_trigger_sound_args *bf_sound = NULL;
|
|
mlan_tx_bf_peer_args *tx_bf_peer = NULL;
|
|
mlan_snr_thr_args *bf_snr = NULL;
|
|
mlan_bf_periodicity_args *bf_periodicity = NULL;
|
|
mlan_bf_global_cfg_args *bf_global = NULL;
|
|
|
|
ENTER();
|
|
|
|
memset(&bf_cfg, 0, sizeof(bf_cfg));
|
|
/* Pointer to corresponding buffer */
|
|
bf_sound = bf_cfg.body.bf_sound;
|
|
tx_bf_peer = bf_cfg.body.tx_bf_peer;
|
|
bf_snr = bf_cfg.body.bf_snr;
|
|
bf_periodicity = bf_cfg.body.bf_periodicity;
|
|
bf_global = &bf_cfg.body.bf_global_cfg;
|
|
|
|
/* Total characters in buffer */
|
|
char_count = data_length - 1;
|
|
memset(buf, 0, sizeof(buf));
|
|
if (char_count) {
|
|
if (data_length > (int)sizeof(buf)) {
|
|
PRINTM(MERROR, "Too many arguments\n");
|
|
ret = -EINVAL;
|
|
goto done;
|
|
}
|
|
if (copy_from_user(buf, wrq->u.data.pointer, data_length)) {
|
|
PRINTM(MERROR, "copy from user failed\n");
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
|
|
if (char_count > 1 && buf[1] != ';') {
|
|
PRINTM(MERROR,
|
|
"No action argument. Separate with ';'\n");
|
|
ret = -EINVAL;
|
|
goto done;
|
|
}
|
|
/* Replace ';' with NULL in the string to separate args */
|
|
for (i = 0; i < char_count; i++) {
|
|
if (buf[i] == ';')
|
|
buf[i] = '\0';
|
|
}
|
|
/* The first byte represents the beamforming action */
|
|
if (woal_atoi(&bf_action, &buf[0]) != MLAN_STATUS_SUCCESS) {
|
|
ret = -EINVAL;
|
|
goto done;
|
|
}
|
|
switch (bf_action) {
|
|
case BF_GLOBAL_CONFIGURATION:
|
|
if (char_count == 1) {
|
|
action = MLAN_ACT_GET;
|
|
bf_cfg.action = BF_CFG_ACT_GET;
|
|
} else {
|
|
action = MLAN_ACT_SET;
|
|
bf_cfg.action = BF_CFG_ACT_SET;
|
|
/* Eliminate action field */
|
|
token = &buf[2];
|
|
for (i = 1, str = &buf[2]; token != NULL; i++) {
|
|
token = strstr(str, " ");
|
|
pos = str;
|
|
if (token != NULL) {
|
|
*token = '\0';
|
|
str = token + 1;
|
|
}
|
|
woal_atoi(&tmp_val, pos);
|
|
switch (i) {
|
|
case BF_ENABLE_PARAM:
|
|
bf_global->bf_enbl =
|
|
(t_u8)tmp_val;
|
|
break;
|
|
case SOUND_ENABLE_PARAM:
|
|
bf_global->sounding_enbl =
|
|
(t_u8)tmp_val;
|
|
break;
|
|
case FB_TYPE_PARAM:
|
|
bf_global->fb_type =
|
|
(t_u8)tmp_val;
|
|
break;
|
|
case SNR_THRESHOLD_PARAM:
|
|
bf_global->snr_threshold =
|
|
(t_u8)tmp_val;
|
|
break;
|
|
case SOUND_INTVL_PARAM:
|
|
bf_global->sounding_interval =
|
|
(t_u16)tmp_val;
|
|
break;
|
|
case BF_MODE_PARAM:
|
|
bf_global->bf_mode =
|
|
(t_u8)tmp_val;
|
|
break;
|
|
default:
|
|
PRINTM(MERROR,
|
|
"Invalid Argument\n");
|
|
ret = -EINVAL;
|
|
goto done;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case TRIGGER_SOUNDING_FOR_PEER:
|
|
/*
|
|
* First arg = 2 BfAction
|
|
* Second arg = 17 MAC "00:50:43:20:BF:64"
|
|
*/
|
|
if (char_count != 19) {
|
|
PRINTM(MERROR, "Invalid argument\n");
|
|
ret = -EINVAL;
|
|
goto done;
|
|
}
|
|
woal_mac2u8(bf_sound->peer_mac, &buf[2]);
|
|
action = MLAN_ACT_SET;
|
|
bf_cfg.action = BF_CFG_ACT_SET;
|
|
break;
|
|
case SET_GET_BF_PERIODICITY:
|
|
/*
|
|
* First arg = 2 BfAction
|
|
* Second arg = 18 MAC "00:50:43:20:BF:64;"
|
|
* Third arg = 1 (min char) TX BF interval
|
|
* 10 (max char) u32 maximum value
|
|
* 4294967295
|
|
*/
|
|
if (char_count < 19 || char_count > 30) {
|
|
PRINTM(MERROR, "Invalid argument\n");
|
|
ret = -EINVAL;
|
|
goto done;
|
|
}
|
|
|
|
woal_mac2u8(bf_periodicity->peer_mac, &buf[2]);
|
|
if (char_count == 19) {
|
|
action = MLAN_ACT_GET;
|
|
bf_cfg.action = BF_CFG_ACT_GET;
|
|
} else {
|
|
action = MLAN_ACT_SET;
|
|
bf_cfg.action = BF_CFG_ACT_SET;
|
|
if (woal_atoi(&interval, &buf[20]) !=
|
|
MLAN_STATUS_SUCCESS) {
|
|
ret = -EINVAL;
|
|
goto done;
|
|
}
|
|
bf_periodicity->interval = interval;
|
|
}
|
|
break;
|
|
case TX_BF_FOR_PEER_ENBL:
|
|
/*
|
|
* Handle only SET operation here
|
|
* First arg = 2 BfAction
|
|
* Second arg = 18 MAC "00:50:43:20:BF:64;"
|
|
* Third arg = 2 enable/disable bf
|
|
* Fourth arg = 2 enable/disable sounding
|
|
* Fifth arg = 1 FB Type
|
|
*/
|
|
if (char_count != 25 && char_count != 1) {
|
|
PRINTM(MERROR, "Invalid argument\n");
|
|
ret = -EINVAL;
|
|
goto done;
|
|
}
|
|
if (char_count == 1) {
|
|
action = MLAN_ACT_GET;
|
|
bf_cfg.action = BF_CFG_ACT_GET;
|
|
} else {
|
|
woal_mac2u8(tx_bf_peer->peer_mac, &buf[2]);
|
|
woal_atoi(&tmp_val, &buf[20]);
|
|
tx_bf_peer->bf_enbl = (t_u8)tmp_val;
|
|
woal_atoi(&tmp_val, &buf[22]);
|
|
tx_bf_peer->sounding_enbl = (t_u8)tmp_val;
|
|
woal_atoi(&tmp_val, &buf[24]);
|
|
tx_bf_peer->fb_type = (t_u8)tmp_val;
|
|
action = MLAN_ACT_SET;
|
|
bf_cfg.action = BF_CFG_ACT_SET;
|
|
}
|
|
break;
|
|
case SET_SNR_THR_PEER:
|
|
/*
|
|
* First arg = 2 BfAction
|
|
* Second arg = 18 MAC "00:50:43:20:BF:64;"
|
|
* Third arg = 1/2 SNR u8 - can be 1/2 charerters
|
|
*/
|
|
if (char_count != 1 &&
|
|
!(char_count == 21 || char_count == 22)) {
|
|
PRINTM(MERROR, "Invalid argument\n");
|
|
ret = -EINVAL;
|
|
goto done;
|
|
}
|
|
if (char_count == 1) {
|
|
action = MLAN_ACT_GET;
|
|
bf_cfg.action = BF_CFG_ACT_GET;
|
|
} else {
|
|
woal_mac2u8(bf_snr->peer_mac, &buf[2]);
|
|
if (woal_atoi(&snr, &buf[20]) !=
|
|
MLAN_STATUS_SUCCESS) {
|
|
ret = -EINVAL;
|
|
goto done;
|
|
}
|
|
bf_snr->snr = snr;
|
|
action = MLAN_ACT_SET;
|
|
bf_cfg.action = BF_CFG_ACT_SET;
|
|
}
|
|
break;
|
|
default:
|
|
ret = -EINVAL;
|
|
goto done;
|
|
}
|
|
|
|
/* Save the value */
|
|
bf_cfg.bf_action = bf_action;
|
|
if (MLAN_STATUS_SUCCESS !=
|
|
woal_set_get_tx_bf_cfg(priv, action, &bf_cfg)) {
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
} else {
|
|
ret = -EINVAL;
|
|
goto done;
|
|
}
|
|
|
|
if (action == MLAN_ACT_GET) {
|
|
data_length = 0;
|
|
memset(buf, 0, sizeof(buf));
|
|
switch (bf_action) {
|
|
case BF_GLOBAL_CONFIGURATION:
|
|
data_length += sprintf(buf + data_length, "%d ",
|
|
(int)bf_global->bf_enbl);
|
|
data_length += sprintf(buf + data_length, "%d ",
|
|
(int)bf_global->sounding_enbl);
|
|
data_length += sprintf(buf + data_length, "%d ",
|
|
(int)bf_global->fb_type);
|
|
data_length += sprintf(buf + data_length, "%d ",
|
|
(int)bf_global->snr_threshold);
|
|
data_length +=
|
|
sprintf(buf + data_length, "%d ",
|
|
(int)bf_global->sounding_interval);
|
|
data_length += sprintf(buf + data_length, "%d ",
|
|
(int)bf_global->bf_mode);
|
|
break;
|
|
case SET_GET_BF_PERIODICITY:
|
|
data_length += sprintf(buf + data_length,
|
|
"%02x:%02x:%02x:%02x:%02x:%02x",
|
|
bf_periodicity->peer_mac[0],
|
|
bf_periodicity->peer_mac[1],
|
|
bf_periodicity->peer_mac[2],
|
|
bf_periodicity->peer_mac[3],
|
|
bf_periodicity->peer_mac[4],
|
|
bf_periodicity->peer_mac[5]);
|
|
data_length += sprintf(buf + data_length, "%c", ' ');
|
|
data_length += sprintf(buf + data_length, "%d",
|
|
bf_periodicity->interval);
|
|
break;
|
|
case TX_BF_FOR_PEER_ENBL:
|
|
for (i = 0; i < (int)bf_cfg.no_of_peers; i++) {
|
|
data_length +=
|
|
sprintf(buf + data_length,
|
|
"%02x:%02x:%02x:%02x:%02x:%02x",
|
|
tx_bf_peer->peer_mac[0],
|
|
tx_bf_peer->peer_mac[1],
|
|
tx_bf_peer->peer_mac[2],
|
|
tx_bf_peer->peer_mac[3],
|
|
tx_bf_peer->peer_mac[4],
|
|
tx_bf_peer->peer_mac[5]);
|
|
data_length +=
|
|
sprintf(buf + data_length, "%c", ' ');
|
|
data_length += sprintf(buf + data_length, "%d;",
|
|
tx_bf_peer->bf_enbl);
|
|
data_length +=
|
|
sprintf(buf + data_length, "%d;",
|
|
tx_bf_peer->sounding_enbl);
|
|
data_length += sprintf(buf + data_length, "%d ",
|
|
tx_bf_peer->fb_type);
|
|
tx_bf_peer++;
|
|
}
|
|
break;
|
|
case SET_SNR_THR_PEER:
|
|
for (i = 0; i < (int)bf_cfg.no_of_peers; i++) {
|
|
data_length +=
|
|
sprintf(buf + data_length,
|
|
"%02x:%02x:%02x:%02x:%02x:%02x",
|
|
bf_snr->peer_mac[0],
|
|
bf_snr->peer_mac[1],
|
|
bf_snr->peer_mac[2],
|
|
bf_snr->peer_mac[3],
|
|
bf_snr->peer_mac[4],
|
|
bf_snr->peer_mac[5]);
|
|
data_length +=
|
|
sprintf(buf + data_length, "%c", ';');
|
|
data_length += sprintf(buf + data_length, "%d",
|
|
bf_snr->snr);
|
|
data_length +=
|
|
sprintf(buf + data_length, "%c", ' ');
|
|
bf_snr++;
|
|
}
|
|
break;
|
|
}
|
|
buf[data_length] = '\0';
|
|
}
|
|
|
|
wrq->u.data.length = data_length;
|
|
if (copy_to_user(wrq->u.data.pointer, buf, wrq->u.data.length)) {
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
|
|
done:
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @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;
|
|
wlan_ioctl_get_scan_table_info *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 =
|
|
(wlan_ioctl_get_scan_table_info __force *)wrq->u.data.pointer;
|
|
pcurrent = (t_u8 *)prsp_info->scan_table_entry_buf;
|
|
|
|
pbuffer_end =
|
|
(t_u8 __force *)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 = %d\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 [%d]\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->scan_number = num_scans_done;
|
|
ret_len = pcurrent - (t_u8 __force *)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/MLAN_STATUS_PENDING -- success,
|
|
* otherwise fail
|
|
*/
|
|
static mlan_status woal_get_scan_table_ioctl(moal_private *priv,
|
|
struct iwreq *wrq)
|
|
{
|
|
mlan_ioctl_req *req = NULL;
|
|
mlan_ds_scan *scan = NULL;
|
|
int scan_start = 0;
|
|
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) {
|
|
status = MLAN_STATUS_FAILURE;
|
|
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");
|
|
goto done;
|
|
}
|
|
if (scan_start > 0)
|
|
scan->sub_command = MLAN_OID_SCAN_NORMAL;
|
|
else
|
|
scan->sub_command = MLAN_OID_SCAN_GET_CURRENT_BSS;
|
|
/* 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 (status != MLAN_STATUS_PENDING)
|
|
kfree(req);
|
|
LEAVE();
|
|
return status;
|
|
}
|
|
|
|
/**
|
|
* @brief Set user scan ext -- Async mode, without wait
|
|
*
|
|
* @param priv A pointer to moal_private structure
|
|
* @param wrq A pointer to iwreq structure
|
|
*
|
|
* @return 0 -- success, otherwise fail
|
|
*/
|
|
static int woal_set_user_scan_ext_ioctl(moal_private *priv, struct iwreq *wrq)
|
|
{
|
|
int ret = 0;
|
|
wlan_user_scan_cfg scan_req;
|
|
ENTER();
|
|
memset(&scan_req, 0x00, sizeof(scan_req));
|
|
if (copy_from_user(&scan_req, wrq->u.data.pointer,
|
|
MIN(wrq->u.data.length, sizeof(scan_req)))) {
|
|
PRINTM(MINFO, "Copy from user failed\n");
|
|
LEAVE();
|
|
return -EFAULT;
|
|
}
|
|
if (MLAN_STATUS_FAILURE == woal_do_scan(priv, &scan_req))
|
|
ret = -EFAULT;
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Set user scan
|
|
*
|
|
* @param priv A pointer to moal_private structure
|
|
* @param wrq A pointer to iwreq structure
|
|
*
|
|
* @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- 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;
|
|
mlan_status status = MLAN_STATUS_SUCCESS;
|
|
union iwreq_data wrqu;
|
|
moal_handle *handle = priv->phandle;
|
|
|
|
ENTER();
|
|
|
|
if (handle->scan_pending_on_block == MTRUE) {
|
|
PRINTM(MINFO, "scan already in processing...\n");
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
if (MOAL_ACQ_SEMAPHORE_BLOCK(&handle->async_sem)) {
|
|
PRINTM(MERROR, "Acquire semaphore error, request_scan\n");
|
|
LEAVE();
|
|
return MLAN_STATUS_FAILURE;
|
|
}
|
|
handle->scan_pending_on_block = MTRUE;
|
|
handle->scan_priv = priv;
|
|
|
|
/* 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");
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
|
|
/* 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);
|
|
}
|
|
|
|
done:
|
|
handle->scan_pending_on_block = MFALSE;
|
|
handle->scan_priv = NULL;
|
|
MOAL_REL_SEMAPHORE(&handle->async_sem);
|
|
if (status != MLAN_STATUS_PENDING)
|
|
kfree(req);
|
|
LEAVE();
|
|
return status;
|
|
}
|
|
|
|
#ifdef SDIO
|
|
/**
|
|
* @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 rw = 0, func, data = 0;
|
|
int buf[3], reg, ret = MLAN_STATUS_SUCCESS;
|
|
int data_length = wrq->u.data.length;
|
|
|
|
ENTER();
|
|
|
|
if (data_length < 2 || data_length > 3) {
|
|
PRINTM(MERROR, "Invalid number of arguments\n");
|
|
ret = -EINVAL;
|
|
goto done;
|
|
}
|
|
|
|
if (copy_from_user(buf, wrq->u.data.pointer,
|
|
sizeof(int) * data_length)) {
|
|
PRINTM(MERROR, "Copy from user failed\n");
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
|
|
func = (t_u8)buf[0];
|
|
if (func > 7) {
|
|
PRINTM(MERROR, "Invalid function number!\n");
|
|
ret = -EINVAL;
|
|
goto done;
|
|
}
|
|
reg = (t_u32)buf[1];
|
|
if (data_length == 2) {
|
|
rw = 0; /* CMD52 read */
|
|
PRINTM(MINFO, "Cmd52 read, func=%d, reg=0x%08X\n", func, reg);
|
|
}
|
|
if (data_length == 3) {
|
|
rw = 1; /* CMD52 write */
|
|
data = (t_u8)buf[2];
|
|
PRINTM(MINFO, "Cmd52 write, func=%d, reg=0x%08X, data=0x%02X\n",
|
|
func, reg, data);
|
|
}
|
|
|
|
if (!rw) {
|
|
#ifdef SDIO_MMC
|
|
sdio_claim_host(
|
|
((struct sdio_mmc_card *)priv->phandle->card)->func);
|
|
if (func)
|
|
data = sdio_readb(
|
|
((struct sdio_mmc_card *)priv->phandle->card)
|
|
->func,
|
|
reg, &ret);
|
|
else
|
|
data = sdio_f0_readb(
|
|
((struct sdio_mmc_card *)priv->phandle->card)
|
|
->func,
|
|
reg, &ret);
|
|
sdio_release_host(
|
|
((struct sdio_mmc_card *)priv->phandle->card)->func);
|
|
if (ret) {
|
|
PRINTM(MERROR,
|
|
"sdio_readb: reading register 0x%X failed\n",
|
|
reg);
|
|
goto done;
|
|
}
|
|
#else
|
|
if (sdio_read_ioreg(priv->phandle->card, func, reg, &data) <
|
|
0) {
|
|
PRINTM(MERROR,
|
|
"sdio_read_ioreg: reading register 0x%X failed\n",
|
|
reg);
|
|
ret = MLAN_STATUS_FAILURE;
|
|
goto done;
|
|
}
|
|
#endif
|
|
} else {
|
|
#ifdef SDIO_MMC
|
|
sdio_claim_host(
|
|
((struct sdio_mmc_card *)priv->phandle->card)->func);
|
|
if (func)
|
|
sdio_writeb(
|
|
((struct sdio_mmc_card *)priv->phandle->card)
|
|
->func,
|
|
data, reg, &ret);
|
|
else
|
|
sdio_f0_writeb(
|
|
((struct sdio_mmc_card *)priv->phandle->card)
|
|
->func,
|
|
data, reg, &ret);
|
|
sdio_release_host(
|
|
((struct sdio_mmc_card *)priv->phandle->card)->func);
|
|
if (ret) {
|
|
PRINTM(MERROR,
|
|
"sdio_writeb: writing register 0x%X failed\n",
|
|
reg);
|
|
goto done;
|
|
}
|
|
#else
|
|
if (sdio_write_ioreg(priv->phandle->card, func, reg, data) <
|
|
0) {
|
|
PRINTM(MERROR,
|
|
"sdio_write_ioreg: writing register 0x%X failed\n",
|
|
reg);
|
|
ret = MLAN_STATUS_FAILURE;
|
|
goto done;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
buf[0] = data;
|
|
wrq->u.data.length = 1;
|
|
if (copy_to_user(wrq->u.data.pointer, buf, sizeof(int))) {
|
|
PRINTM(MERROR, "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, mode;
|
|
t_u16 blklen = 0, blknum = 0;
|
|
int reg = 0, pattern_len = 0, pos = 0, ret = MLAN_STATUS_SUCCESS;
|
|
t_u32 total_len = 0;
|
|
t_u8 *data = NULL;
|
|
|
|
ENTER();
|
|
|
|
buf = kmalloc(WOAL_2K_BYTES, GFP_KERNEL);
|
|
if (!buf) {
|
|
PRINTM(MERROR, "Cannot allocate buffer for command!\n");
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
data = kmalloc(WOAL_2K_BYTES, GFP_KERNEL);
|
|
if (!data) {
|
|
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) */
|
|
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, addr = %#x, mode = %d, block size = %d, block(byte) number = %d\n",
|
|
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, total_len)) {
|
|
PRINTM(MINFO, "Copy to user failed\n");
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
wrq->u.data.length = total_len;
|
|
} else {
|
|
pattern_len = wrq->u.data.length - 11;
|
|
if (pattern_len > (int)total_len)
|
|
pattern_len = total_len;
|
|
memset(data, 0, WOAL_2K_BYTES);
|
|
|
|
/* Copy/duplicate the pattern to data buffer */
|
|
for (pos = 0; pos < (int)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:
|
|
kfree(buf);
|
|
kfree(data);
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
#endif /* SDIO */
|
|
|
|
#ifdef SDIO
|
|
/**
|
|
* @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/MLAN_STATUS_PENDING --success, otherwise fail
|
|
*/
|
|
static int woal_do_sdio_mpa_ctrl(moal_private *priv, struct iwreq *wrq)
|
|
{
|
|
int data[6], data_length = wrq->u.data.length, copy_len;
|
|
int ret = 0;
|
|
mlan_ds_misc_cfg *misc = NULL;
|
|
mlan_ioctl_req *req = NULL;
|
|
mlan_status status = MLAN_STATUS_SUCCESS;
|
|
|
|
ENTER();
|
|
|
|
if (sizeof(int) * wrq->u.data.length > sizeof(data)) {
|
|
PRINTM(MERROR, "Too many arguments\n");
|
|
ret = -EINVAL;
|
|
goto done;
|
|
}
|
|
copy_len = MIN(sizeof(data), sizeof(int) * data_length);
|
|
|
|
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;
|
|
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
|
|
if (status != MLAN_STATUS_SUCCESS) {
|
|
PRINTM(MERROR, "woal_request_ioctl returned %d\n", ret);
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
|
|
if (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 = ARRAY_SIZE(data);
|
|
goto done;
|
|
}
|
|
|
|
if (copy_from_user(data, wrq->u.data.pointer, copy_len)) {
|
|
PRINTM(MINFO, "Copy from user failed\n");
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
|
|
switch (data_length) {
|
|
case 6:
|
|
misc->param.mpa_ctrl.rx_max_ports = data[5];
|
|
/* fall through */
|
|
case 5:
|
|
misc->param.mpa_ctrl.tx_max_ports = data[4];
|
|
/* fall through */
|
|
case 4:
|
|
misc->param.mpa_ctrl.rx_buf_size = data[3];
|
|
/* fall through */
|
|
case 3:
|
|
misc->param.mpa_ctrl.tx_buf_size = data[2];
|
|
/* fall through */
|
|
case 2:
|
|
misc->param.mpa_ctrl.rx_enable = data[1];
|
|
/* fall through */
|
|
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;
|
|
}
|
|
|
|
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
|
|
if (status != MLAN_STATUS_SUCCESS) {
|
|
PRINTM(MERROR, "woal_request_ioctl returned %d\n", ret);
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
|
|
done:
|
|
if (status != MLAN_STATUS_PENDING)
|
|
kfree(req);
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* @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 ret = 0;
|
|
int data[7], copy_len;
|
|
mlan_ds_scan *scan = NULL;
|
|
mlan_ioctl_req *req = NULL;
|
|
int data_length = wrq->u.data.length;
|
|
mlan_status status = MLAN_STATUS_SUCCESS;
|
|
int arg_len = 0;
|
|
|
|
ENTER();
|
|
arg_len = 7;
|
|
if (data_length > arg_len) {
|
|
PRINTM(MERROR, "Too much arguments\n");
|
|
LEAVE();
|
|
return -EINVAL;
|
|
}
|
|
copy_len = sizeof(int) * data_length;
|
|
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_scan));
|
|
if (req == NULL) {
|
|
LEAVE();
|
|
return -ENOMEM;
|
|
}
|
|
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 (data_length) {
|
|
if (copy_from_user(data, wrq->u.data.pointer, copy_len)) {
|
|
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;
|
|
}
|
|
if ((data[6] < 0) || (data[6] > 1)) {
|
|
PRINTM(MERROR, "Invalid argument for extended scan\n");
|
|
ret = -EINVAL;
|
|
goto done;
|
|
}
|
|
req->action = MLAN_ACT_SET;
|
|
moal_memcpy_ext(priv->phandle, &scan->param.scan_cfg, data,
|
|
sizeof(data), sizeof(scan->param.scan_cfg));
|
|
} else
|
|
req->action = MLAN_ACT_GET;
|
|
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
|
|
if (status != MLAN_STATUS_SUCCESS) {
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
if (!data_length) {
|
|
moal_memcpy_ext(priv->phandle, data, &scan->param.scan_cfg,
|
|
sizeof(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 = arg_len;
|
|
}
|
|
done:
|
|
if (status != MLAN_STATUS_PENDING)
|
|
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], copy_len, ret = 0;
|
|
mlan_ds_pm_cfg *pm_cfg = NULL;
|
|
mlan_ioctl_req *req = NULL;
|
|
int allowed = 3;
|
|
int i = 3;
|
|
int data_length = wrq->u.data.length;
|
|
mlan_status status = MLAN_STATUS_SUCCESS;
|
|
|
|
ENTER();
|
|
|
|
allowed++; /* For beacon missing timeout parameter */
|
|
allowed += 2; /* For delay to PS and PS mode parameters */
|
|
copy_len = MIN(sizeof(data), sizeof(int) * data_length);
|
|
|
|
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_pm_cfg));
|
|
if (req == NULL) {
|
|
ret = -ENOMEM;
|
|
goto done;
|
|
}
|
|
if (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 (data_length) {
|
|
if (copy_from_user(data, wrq->u.data.pointer, copy_len)) {
|
|
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] != MRVDRV_IGNORE_MULTIPLE_DTIM) &&
|
|
(data[1] != MRVDRV_MATCH_CLOSEST_DTIM) &&
|
|
((data[1] < MRVDRV_MIN_MULTIPLE_DTIM) ||
|
|
(data[1] > MRVDRV_MAX_MULTIPLE_DTIM))) {
|
|
PRINTM(MERROR, "Invalid argument for multiple DTIM\n");
|
|
ret = -EINVAL;
|
|
goto done;
|
|
}
|
|
|
|
if ((data[2] < MRVDRV_MIN_LISTEN_INTERVAL) &&
|
|
(data[2] != MRVDRV_LISTEN_INTERVAL_DISABLE)) {
|
|
PRINTM(MERROR,
|
|
"Invalid argument for listen interval\n");
|
|
ret = -EINVAL;
|
|
goto done;
|
|
}
|
|
|
|
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 (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;
|
|
moal_memcpy_ext(priv->phandle, &pm_cfg->param.ps_cfg, data,
|
|
sizeof(data), sizeof(pm_cfg->param.ps_cfg));
|
|
} else
|
|
req->action = MLAN_ACT_GET;
|
|
|
|
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
|
|
if (status != MLAN_STATUS_SUCCESS) {
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
moal_memcpy_ext(priv->phandle, data, &pm_cfg->param.ps_cfg,
|
|
MIN((sizeof(int) * allowed),
|
|
sizeof(pm_cfg->param.ps_cfg)),
|
|
sizeof(data));
|
|
if (copy_to_user(wrq->u.data.pointer, data, sizeof(int) * allowed)) {
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
wrq->u.data.length = allowed;
|
|
|
|
if (req->action == MLAN_ACT_SET) {
|
|
pm_cfg = (mlan_ds_pm_cfg *)req->pbuf;
|
|
pm_cfg->sub_command = MLAN_OID_PM_CFG_IEEE_PS;
|
|
pm_cfg->param.ps_mode = 1;
|
|
req->req_id = MLAN_IOCTL_PM_CFG;
|
|
req->action = MLAN_ACT_SET;
|
|
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
|
|
}
|
|
|
|
done:
|
|
if (status != MLAN_STATUS_PENDING)
|
|
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. 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)
|
|
{
|
|
mlan_ioctl_req *req = NULL;
|
|
mlan_ds_wmm_cfg *cfg = NULL;
|
|
wlan_ioctl_wmm_addts_req_t addts_ioctl;
|
|
int ret = 0;
|
|
mlan_status status = MLAN_STATUS_SUCCESS;
|
|
|
|
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, 0x00, 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;
|
|
}
|
|
|
|
cfg->param.addts.timeout = addts_ioctl.timeout_ms;
|
|
cfg->param.addts.ie_data_len = addts_ioctl.ie_data_len;
|
|
|
|
if (cfg->param.addts.ie_data_len >
|
|
sizeof(cfg->param.addts.ie_data)) {
|
|
PRINTM(MERROR, "IE data length too large\n");
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
|
|
moal_memcpy_ext(priv->phandle, cfg->param.addts.ie_data,
|
|
addts_ioctl.ie_data,
|
|
cfg->param.addts.ie_data_len,
|
|
sizeof(cfg->param.addts.ie_data));
|
|
|
|
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
|
|
if (status != MLAN_STATUS_SUCCESS) {
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
addts_ioctl.cmd_result = cfg->param.addts.result;
|
|
addts_ioctl.ieee_status_code =
|
|
(t_u8)cfg->param.addts.status_code;
|
|
addts_ioctl.ie_data_len = cfg->param.addts.ie_data_len;
|
|
|
|
moal_memcpy_ext(priv->phandle, addts_ioctl.ie_data,
|
|
cfg->param.addts.ie_data,
|
|
cfg->param.addts.ie_data_len,
|
|
sizeof(addts_ioctl.ie_data));
|
|
|
|
wrq->u.data.length =
|
|
(sizeof(addts_ioctl) - sizeof(addts_ioctl.ie_data) +
|
|
cfg->param.addts.ie_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 (status != MLAN_STATUS_PENDING)
|
|
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;
|
|
mlan_status status = MLAN_STATUS_SUCCESS;
|
|
|
|
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, 0x00, 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;
|
|
cfg->param.delts.ie_data_len = (t_u8)delts_ioctl.ie_data_len;
|
|
|
|
if ((cfg->param.delts.ie_data_len) >
|
|
sizeof(cfg->param.delts.ie_data)) {
|
|
PRINTM(MERROR, "IE data length too large\n");
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
|
|
moal_memcpy_ext(priv->phandle, cfg->param.delts.ie_data,
|
|
delts_ioctl.ie_data,
|
|
cfg->param.delts.ie_data_len,
|
|
sizeof(cfg->param.delts.ie_data));
|
|
|
|
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
|
|
if (status != MLAN_STATUS_SUCCESS) {
|
|
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 (status != MLAN_STATUS_PENDING)
|
|
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;
|
|
mlan_status status = MLAN_STATUS_SUCCESS;
|
|
|
|
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, 0x00, 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;
|
|
|
|
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
|
|
if (status != MLAN_STATUS_SUCCESS) {
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
memset(&qcfg_ioctl, 0x00, 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 (status != MLAN_STATUS_PENDING)
|
|
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;
|
|
mlan_status status = MLAN_STATUS_SUCCESS;
|
|
|
|
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, 0x00, 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;
|
|
}
|
|
|
|
moal_memcpy_ext(priv->phandle, (void *)pqstats,
|
|
(void *)&qstats_ioctl, sizeof(qstats_ioctl),
|
|
sizeof(qstats_ioctl));
|
|
PRINTM(MINFO, "QSTATS: IOCTL [%d,%d]\n", qstats_ioctl.action,
|
|
qstats_ioctl.user_priority);
|
|
|
|
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
|
|
if (status != MLAN_STATUS_SUCCESS) {
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
|
|
memset(&qstats_ioctl, 0x00, sizeof(qstats_ioctl));
|
|
moal_memcpy_ext(priv->phandle, (void *)&qstats_ioctl,
|
|
(void *)pqstats, sizeof(qstats_ioctl),
|
|
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 (status != MLAN_STATUS_PENDING)
|
|
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;
|
|
mlan_status status = MLAN_STATUS_SUCCESS;
|
|
|
|
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)) {
|
|
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
|
|
if (status != MLAN_STATUS_SUCCESS) {
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
|
|
memset(&qstatus_ioctl, 0x00, sizeof(qstatus_ioctl));
|
|
moal_memcpy_ext(priv->phandle, (void *)&qstatus_ioctl,
|
|
(void *)&pwmm->param.q_status,
|
|
sizeof(qstatus_ioctl), 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 (status != MLAN_STATUS_PENDING)
|
|
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;
|
|
mlan_status status = MLAN_STATUS_SUCCESS;
|
|
|
|
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, 0x00, 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, "TS_STATUS: copy from user failed\n");
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
|
|
memset(&pwmm->param.ts_status, 0x00, sizeof(ts_status_ioctl));
|
|
pwmm->param.ts_status.tid = ts_status_ioctl.tid;
|
|
|
|
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
|
|
if (status != MLAN_STATUS_SUCCESS) {
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
|
|
memset(&ts_status_ioctl, 0x00, sizeof(ts_status_ioctl));
|
|
moal_memcpy_ext(priv->phandle, (void *)&ts_status_ioctl,
|
|
(void *)&pwmm->param.ts_status,
|
|
sizeof(ts_status_ioctl),
|
|
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, "TS_STATUS: copy to user failed\n");
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
|
|
done:
|
|
if (status != MLAN_STATUS_PENDING)
|
|
kfree(req);
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Private IOCTL entry to get the By-passed TX packet from upper layer
|
|
*
|
|
* @param priv Pointer to the moal_private driver data struct
|
|
* @param wrq A pointer to iwreq structure containing the packet
|
|
*
|
|
* @return 0 if successful; IOCTL error code otherwise
|
|
*/
|
|
static int woal_bypassed_packet_ioctl(moal_private *priv, struct iwreq *wrq)
|
|
{
|
|
int ret = 0;
|
|
struct sk_buff *skb = NULL;
|
|
struct ethhdr *eth;
|
|
t_u16 moreLen = 0, copyLen = 0;
|
|
ENTER();
|
|
|
|
#define MLAN_BYPASS_PKT_EXTRA_OFFSET (4)
|
|
|
|
copyLen = wrq->u.data.length;
|
|
moreLen = MLAN_MIN_DATA_HEADER_LEN + MLAN_BYPASS_PKT_EXTRA_OFFSET +
|
|
sizeof(mlan_buffer);
|
|
|
|
skb = alloc_skb(copyLen + moreLen, GFP_KERNEL);
|
|
if (skb == NULL) {
|
|
PRINTM(MERROR, "kmalloc no memory !!\n");
|
|
LEAVE();
|
|
return -ENOMEM;
|
|
}
|
|
|
|
skb_reserve(skb, moreLen);
|
|
|
|
if (copy_from_user(skb_put(skb, copyLen), wrq->u.data.pointer,
|
|
copyLen)) {
|
|
PRINTM(MERROR, "PortBlock: copy from user failed\n");
|
|
dev_kfree_skb_any(skb);
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
|
|
eth = (struct ethhdr *)skb->data;
|
|
eth->h_proto = __constant_htons(eth->h_proto);
|
|
skb->dev = priv->netdev;
|
|
|
|
HEXDUMP("Bypass TX Data", skb->data, MIN(skb->len, 100));
|
|
|
|
woal_hard_start_xmit(skb, priv->netdev);
|
|
done:
|
|
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;
|
|
}
|
|
|
|
/**
|
|
* @brief Set/Get Port Control mode
|
|
*
|
|
* @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_port_ctrl(moal_private *priv, struct iwreq *wrq)
|
|
{
|
|
mlan_ioctl_req *req = NULL;
|
|
mlan_ds_sec_cfg *sec = NULL;
|
|
int ret = 0;
|
|
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_PORT_CTRL_ENABLED;
|
|
req->req_id = MLAN_IOCTL_SEC_CFG;
|
|
|
|
if (wrq->u.data.length) {
|
|
if (copy_from_user(&sec->param.port_ctrl_enabled,
|
|
wrq->u.data.pointer, sizeof(int)) != 0) {
|
|
PRINTM(MERROR, "port_ctrl:Copy from user failed\n");
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
req->action = MLAN_ACT_SET;
|
|
} else {
|
|
req->action = MLAN_ACT_GET;
|
|
}
|
|
|
|
/* Send IOCTL request to MLAN */
|
|
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
|
|
if (status != MLAN_STATUS_SUCCESS) {
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
|
|
if (!wrq->u.data.length) {
|
|
if (copy_to_user(wrq->u.data.pointer,
|
|
&sec->param.port_ctrl_enabled, sizeof(int))) {
|
|
PRINTM(MERROR, "port_ctrl:Copy to user failed\n");
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
wrq->u.data.length = 1;
|
|
}
|
|
|
|
done:
|
|
if (status != MLAN_STATUS_PENDING)
|
|
kfree(req);
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Set/Get DFS Testing settings
|
|
*
|
|
* @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_dfs_testing(moal_private *priv, struct iwreq *wrq)
|
|
{
|
|
mlan_ioctl_req *req = NULL;
|
|
mlan_ds_11h_cfg *ds_11hcfg = NULL;
|
|
int ret = 0;
|
|
int data[4], copy_len;
|
|
int data_length = wrq->u.data.length;
|
|
mlan_status status = MLAN_STATUS_SUCCESS;
|
|
ENTER();
|
|
|
|
copy_len = MIN(sizeof(data), sizeof(int) * data_length);
|
|
/* Allocate an IOCTL request buffer */
|
|
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11h_cfg));
|
|
if (req == NULL) {
|
|
ret = -ENOMEM;
|
|
goto done;
|
|
}
|
|
|
|
/* Fill request buffer */
|
|
ds_11hcfg = (mlan_ds_11h_cfg *)req->pbuf;
|
|
ds_11hcfg->sub_command = MLAN_OID_11H_DFS_TESTING;
|
|
req->req_id = MLAN_IOCTL_11H_CFG;
|
|
|
|
if (!data_length) {
|
|
req->action = MLAN_ACT_GET;
|
|
} else if (data_length == 4) {
|
|
if (copy_from_user(data, wrq->u.data.pointer, copy_len)) {
|
|
PRINTM(MERROR, "Copy from user failed\n");
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
if ((unsigned)data[0] > 1800) {
|
|
PRINTM(MERROR, "The maximum user CAC is 1800 sec.\n");
|
|
ret = -EINVAL;
|
|
goto done;
|
|
}
|
|
if ((unsigned)data[1] > 0xFFFF) {
|
|
PRINTM(MERROR, "The maximum user NOP is 65535 sec.\n");
|
|
ret = -EINVAL;
|
|
goto done;
|
|
}
|
|
if ((unsigned)data[3] > 0xFF) {
|
|
PRINTM(MERROR,
|
|
"The maximum user fixed channel is 255.\n");
|
|
ret = -EINVAL;
|
|
goto done;
|
|
}
|
|
ds_11hcfg->param.dfs_testing.usr_cac_period_msec =
|
|
(t_u16)data[0];
|
|
ds_11hcfg->param.dfs_testing.usr_nop_period_sec =
|
|
(t_u16)data[1];
|
|
ds_11hcfg->param.dfs_testing.usr_no_chan_change =
|
|
data[2] ? 1 : 0;
|
|
ds_11hcfg->param.dfs_testing.usr_fixed_new_chan = (t_u8)data[3];
|
|
priv->phandle->cac_period_jiffies = (t_u16)data[0] * HZ;
|
|
req->action = MLAN_ACT_SET;
|
|
} else {
|
|
PRINTM(MERROR, "Invalid number of args!\n");
|
|
ret = -EINVAL;
|
|
goto done;
|
|
}
|
|
|
|
/* Send IOCTL request to MLAN */
|
|
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
|
|
if (status != MLAN_STATUS_SUCCESS) {
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
|
|
if (!data_length) {
|
|
data[0] = ds_11hcfg->param.dfs_testing.usr_cac_period_msec;
|
|
data[1] = ds_11hcfg->param.dfs_testing.usr_nop_period_sec;
|
|
data[2] = ds_11hcfg->param.dfs_testing.usr_no_chan_change;
|
|
data[3] = ds_11hcfg->param.dfs_testing.usr_fixed_new_chan;
|
|
if (copy_to_user(wrq->u.data.pointer, &data, sizeof(int) * 4)) {
|
|
PRINTM(MERROR, "Copy to user failed\n");
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
wrq->u.data.length = 4;
|
|
}
|
|
|
|
done:
|
|
if (status != MLAN_STATUS_PENDING)
|
|
kfree(req);
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Set/Get Mgmt Frame passthru mask
|
|
*
|
|
* @param priv A pointer to moal_private structure
|
|
* @param wrq A pointer to iwreq structure
|
|
*
|
|
* @return 0 -- success, otherwise fail
|
|
*/
|
|
static int woal_mgmt_frame_passthru_ctrl(moal_private *priv, struct iwreq *wrq)
|
|
{
|
|
int ret = 0, data_length = wrq->u.data.length;
|
|
mlan_ioctl_req *req = NULL;
|
|
mlan_ds_misc_cfg *mgmt_cfg = NULL;
|
|
int mask = 0;
|
|
mlan_status status = MLAN_STATUS_SUCCESS;
|
|
|
|
ENTER();
|
|
|
|
if (data_length > 1) {
|
|
PRINTM(MERROR, "Invalid no of arguments!\n");
|
|
ret = -EINVAL;
|
|
goto done;
|
|
}
|
|
/* Allocate an IOCTL request buffer */
|
|
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg));
|
|
if (req == NULL) {
|
|
ret = -ENOMEM;
|
|
goto done;
|
|
}
|
|
|
|
/* Fill request buffer */
|
|
mgmt_cfg = (mlan_ds_misc_cfg *)req->pbuf;
|
|
req->req_id = MLAN_IOCTL_MISC_CFG;
|
|
mgmt_cfg->sub_command = MLAN_OID_MISC_RX_MGMT_IND;
|
|
|
|
if (data_length) { /* SET */
|
|
if (copy_from_user(&mask, wrq->u.data.pointer, sizeof(int))) {
|
|
PRINTM(MERROR, "copy from user failed\n");
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
mgmt_cfg->param.mgmt_subtype_mask = mask;
|
|
req->action = MLAN_ACT_SET;
|
|
} else {
|
|
req->action = MLAN_ACT_GET;
|
|
}
|
|
|
|
/* Send IOCTL request to MLAN */
|
|
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
|
|
if (status != MLAN_STATUS_SUCCESS) {
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
|
|
mask = mgmt_cfg->param.mgmt_subtype_mask;
|
|
if (copy_to_user(wrq->u.data.pointer, &mask, sizeof(int))) {
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
wrq->u.data.length = 1;
|
|
|
|
done:
|
|
if (status != MLAN_STATUS_PENDING)
|
|
kfree(req);
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Set/Get CFP table codes
|
|
*
|
|
* @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_cfp_code(moal_private *priv, struct iwreq *wrq)
|
|
{
|
|
int ret = 0;
|
|
int data[2], copy_len;
|
|
int data_length = wrq->u.data.length;
|
|
mlan_ioctl_req *req = NULL;
|
|
mlan_ds_misc_cfg *misc_cfg = NULL;
|
|
mlan_ds_misc_cfp_code *cfp_code = NULL;
|
|
mlan_status status = MLAN_STATUS_SUCCESS;
|
|
|
|
ENTER();
|
|
|
|
if (data_length > 2) {
|
|
PRINTM(MERROR, "Invalid number of argument!\n");
|
|
ret = -EINVAL;
|
|
goto done;
|
|
}
|
|
copy_len = MIN(sizeof(data), sizeof(int) * data_length);
|
|
|
|
/* Allocate an IOCTL request buffer */
|
|
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg));
|
|
if (req == NULL) {
|
|
ret = -ENOMEM;
|
|
goto done;
|
|
}
|
|
|
|
/* Fill request buffer */
|
|
misc_cfg = (mlan_ds_misc_cfg *)req->pbuf;
|
|
cfp_code = &misc_cfg->param.cfp_code;
|
|
misc_cfg->sub_command = MLAN_OID_MISC_CFP_CODE;
|
|
req->req_id = MLAN_IOCTL_MISC_CFG;
|
|
|
|
if (!data_length) {
|
|
req->action = MLAN_ACT_GET;
|
|
} else {
|
|
if (copy_from_user(data, wrq->u.data.pointer, copy_len)) {
|
|
PRINTM(MERROR, "Copy from user failed\n");
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
cfp_code->cfp_code_bg = data[0];
|
|
if (data_length == 2)
|
|
cfp_code->cfp_code_a = data[1];
|
|
req->action = MLAN_ACT_SET;
|
|
}
|
|
|
|
/* Send IOCTL request to MLAN */
|
|
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
|
|
if (status != MLAN_STATUS_SUCCESS) {
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
|
|
if (!data_length) {
|
|
data[0] = cfp_code->cfp_code_bg;
|
|
data[1] = cfp_code->cfp_code_a;
|
|
data_length = 2;
|
|
if (copy_to_user(wrq->u.data.pointer, &data,
|
|
sizeof(int) * data_length)) {
|
|
PRINTM(MERROR, "Copy to user failed\n");
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
wrq->u.data.length = data_length;
|
|
}
|
|
|
|
done:
|
|
if (status != MLAN_STATUS_PENDING)
|
|
kfree(req);
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @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[3] = {0};
|
|
mlan_status status = MLAN_STATUS_SUCCESS;
|
|
int copy_len;
|
|
|
|
ENTER();
|
|
|
|
if (wrq->u.data.length > 2) {
|
|
PRINTM(MERROR, "Invalid number of argument!\n");
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
if (wrq->u.data.length * sizeof(int) > sizeof(data)) {
|
|
PRINTM(MERROR, "Too many arguments\n");
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
copy_len = MIN(sizeof(data), wrq->u.data.length * sizeof(int));
|
|
|
|
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, copy_len)) {
|
|
PRINTM(MERROR, "Copy from user failed\n");
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
|
|
if (priv->phandle->feature_control & FEATURE_CTRL_STREAM_2X2) {
|
|
radio->param.ant_cfg.tx_antenna = data[0];
|
|
radio->param.ant_cfg.rx_antenna = data[0];
|
|
if (wrq->u.data.length == 2)
|
|
radio->param.ant_cfg.rx_antenna = data[1];
|
|
} else {
|
|
radio->param.ant_cfg_1x1.antenna = data[0];
|
|
if (wrq->u.data.length == 2)
|
|
radio->param.ant_cfg_1x1.evaluate_time =
|
|
data[1];
|
|
}
|
|
req->action = MLAN_ACT_SET;
|
|
} else
|
|
req->action = MLAN_ACT_GET;
|
|
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
|
|
if (status != MLAN_STATUS_SUCCESS) {
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
if (!wrq->u.data.length) {
|
|
wrq->u.data.length = 1;
|
|
if (priv->phandle->feature_control & FEATURE_CTRL_STREAM_2X2) {
|
|
data[0] = radio->param.ant_cfg.tx_antenna;
|
|
data[1] = radio->param.ant_cfg.rx_antenna;
|
|
if (data[0] && data[1] && (data[0] != data[1]))
|
|
wrq->u.data.length = 2;
|
|
} else {
|
|
data[0] = (int)radio->param.ant_cfg_1x1.antenna;
|
|
data[1] = (int)radio->param.ant_cfg_1x1.evaluate_time;
|
|
data[2] = (int)radio->param.ant_cfg_1x1.current_antenna;
|
|
if (data[0] == 0xffff && data[2] > 0)
|
|
wrq->u.data.length = 3;
|
|
else if (data[0] == 0xffff)
|
|
wrq->u.data.length = 2;
|
|
}
|
|
if (copy_to_user(wrq->u.data.pointer, data,
|
|
wrq->u.data.length * sizeof(int))) {
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
}
|
|
done:
|
|
if (status != MLAN_STATUS_PENDING)
|
|
kfree(req);
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Configure gpio independent reset
|
|
*
|
|
* @param priv A pointer to moal_private structure
|
|
* @param req A pointer to ifreq structure
|
|
*
|
|
* @return 0 --success, otherwise fail
|
|
*/
|
|
static int woal_ind_rst_ioctl(moal_private *priv, struct iwreq *wrq)
|
|
{
|
|
int data[2], data_length = wrq->u.data.length, copy_len;
|
|
int ret = 0;
|
|
mlan_ds_misc_cfg *misc = NULL;
|
|
mlan_ioctl_req *req = NULL;
|
|
mlan_status status = MLAN_STATUS_SUCCESS;
|
|
|
|
ENTER();
|
|
|
|
if (sizeof(int) * wrq->u.data.length > sizeof(data)) {
|
|
PRINTM(MERROR, "Too many arguments\n");
|
|
ret = -EINVAL;
|
|
goto done;
|
|
}
|
|
copy_len = MIN(sizeof(data), sizeof(int) * data_length);
|
|
|
|
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_IND_RST_CFG;
|
|
req->req_id = MLAN_IOCTL_MISC_CFG;
|
|
|
|
if (data_length == 0) {
|
|
req->action = MLAN_ACT_GET;
|
|
} else if ((data_length == 1) || (data_length == 2)) {
|
|
req->action = MLAN_ACT_SET;
|
|
|
|
if (copy_from_user(data, wrq->u.data.pointer, copy_len)) {
|
|
/* copy_from_user failed */
|
|
PRINTM(MERROR, "S_PARAMS: copy from user failed\n");
|
|
ret = -EINVAL;
|
|
goto done;
|
|
}
|
|
|
|
/* ir_mode */
|
|
if (data[0] < 0 || data[0] > 2) {
|
|
PRINTM(MERROR, "Invalid ir mode parameter (0/1/2)!\n");
|
|
ret = -EINVAL;
|
|
goto done;
|
|
}
|
|
misc->param.ind_rst_cfg.ir_mode = data[0];
|
|
|
|
/* gpio_pin */
|
|
if (data_length == 2) {
|
|
if ((data[1] != 0xFF) && (data[1] < 0)) {
|
|
PRINTM(MERROR, "Invalid gpio pin no !\n");
|
|
ret = -EINVAL;
|
|
goto done;
|
|
}
|
|
misc->param.ind_rst_cfg.gpio_pin = data[1];
|
|
}
|
|
|
|
} else {
|
|
ret = -EINVAL;
|
|
goto done;
|
|
}
|
|
|
|
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
|
|
if (status != MLAN_STATUS_SUCCESS) {
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
|
|
data[0] = misc->param.ind_rst_cfg.ir_mode;
|
|
data[1] = misc->param.ind_rst_cfg.gpio_pin;
|
|
wrq->u.data.length = 2;
|
|
|
|
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 (status != MLAN_STATUS_PENDING)
|
|
kfree(req);
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
/********************************************************
|
|
Global Functions
|
|
********************************************************/
|
|
/**
|
|
* @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_wext_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;
|
|
|
|
if (!IS_STA_WEXT(priv->phandle->params.cfg80211_wext))
|
|
return -EOPNOTSUPP;
|
|
|
|
ENTER();
|
|
|
|
PRINTM(MINFO, "woal_wext_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;
|
|
#ifdef USB
|
|
#ifdef CONFIG_USB_SUSPEND
|
|
case WOAL_USB_SUSPEND:
|
|
ret = woal_enter_usb_suspend(priv->phandle);
|
|
break;
|
|
case WOAL_USB_RESUME:
|
|
ret = woal_exit_usb_suspend(priv->phandle);
|
|
break;
|
|
#endif /* CONFIG_USB_SUSPEND */
|
|
#endif
|
|
case WOAL_11D_CLR_CHAN_TABLE:
|
|
ret = woal_11d_clr_chan_table(priv, wrq);
|
|
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_QOS_CFG:
|
|
ret = woal_set_get_qos_cfg(priv, wrq);
|
|
break;
|
|
#if defined(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;
|
|
#ifndef OPCHAN
|
|
case WOAL_SET_GET_WWS_CFG:
|
|
ret = woal_wws_cfg(priv, wrq);
|
|
break;
|
|
#endif
|
|
case WOAL_SLEEP_PD:
|
|
ret = woal_sleep_pd(priv, wrq);
|
|
break;
|
|
case WOAL_AUTH_TYPE:
|
|
ret = woal_auth_type(priv, wrq);
|
|
break;
|
|
case WOAL_PORT_CTRL:
|
|
ret = woal_port_ctrl(priv, wrq);
|
|
break;
|
|
#ifdef WIFI_DIRECT_SUPPORT
|
|
#if defined(STA_SUPPORT) && defined(UAP_SUPPORT)
|
|
case WOAL_SET_GET_BSS_ROLE:
|
|
ret = woal_set_get_bss_role(priv, wrq);
|
|
break;
|
|
#endif
|
|
#endif
|
|
case WOAL_SET_GET_11H_LOCAL_PWR_CONSTRAINT:
|
|
ret = woal_set_get_11h_local_pwr_constraint(priv, wrq);
|
|
break;
|
|
case WOAL_HT_STREAM_CFG:
|
|
ret = woal_ht_stream_cfg_ioctl(priv, wrq);
|
|
break;
|
|
case WOAL_MAC_CONTROL:
|
|
ret = woal_mac_control_ioctl(priv, wrq);
|
|
break;
|
|
case WOAL_THERMAL:
|
|
ret = woal_thermal_ioctl(priv, wrq);
|
|
break;
|
|
case WOAL_CFG_HOTSPOT:
|
|
ret = woal_cfg_hotspot(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_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_TX_BF_CAP:
|
|
ret = woal_tx_bf_cap_ioctl(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;
|
|
#ifdef SDIO
|
|
case WOAL_SDIO_CLOCK:
|
|
ret = woal_sdio_clock_ioctl(priv, wrq);
|
|
break;
|
|
case WOAL_CMD_52RDWR:
|
|
ret = woal_cmd52rdwr_ioctl(priv, wrq);
|
|
break;
|
|
case WOAL_SDIO_MPA_CTRL:
|
|
ret = woal_do_sdio_mpa_ctrl(priv, wrq);
|
|
break;
|
|
#endif
|
|
case WOAL_BAND_CFG:
|
|
ret = woal_band_cfg(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;
|
|
case WOAL_SLEEP_PARAMS:
|
|
ret = woal_sleep_params_ioctl(priv, wrq);
|
|
break;
|
|
case WOAL_DFS_TESTING:
|
|
ret = woal_dfs_testing(priv, wrq);
|
|
break;
|
|
case WOAL_MGMT_FRAME_CTRL:
|
|
ret = woal_mgmt_frame_passthru_ctrl(priv, wrq);
|
|
break;
|
|
case WOAL_CFP_CODE:
|
|
ret = woal_cfp_code(priv, wrq);
|
|
break;
|
|
case WOAL_SET_GET_TX_RX_ANT:
|
|
ret = woal_set_get_tx_rx_ant(priv, wrq);
|
|
break;
|
|
case WOAL_IND_RST_CFG:
|
|
ret = woal_ind_rst_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_GET_KEY:
|
|
ret = woal_get_key_ioctl(priv, wrq);
|
|
break;
|
|
case WOAL_ASSOCIATE:
|
|
ret = woal_associate_ssid_bssid(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;
|
|
case WOAL_IP_ADDRESS:
|
|
ret = woal_set_get_ip_addr(priv, wrq);
|
|
break;
|
|
case WOAL_TX_BF_CFG:
|
|
ret = woal_tx_bf_cfg_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;
|
|
|
|
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) {
|
|
#ifdef SDIO
|
|
case WOAL_CMD_53RDWR:
|
|
ret = woal_cmd53rdwr_ioctl(priv, wrq);
|
|
break;
|
|
#endif
|
|
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_SET_USER_SCAN_EXT:
|
|
ret = woal_set_user_scan_ext_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;
|
|
case WOAL_BYPASSED_PACKET:
|
|
ret = woal_bypassed_packet_ioctl(priv, wrq);
|
|
break;
|
|
default:
|
|
ret = -EINVAL;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
#ifdef UAP_WEXT
|
|
case WOAL_FROYO_START:
|
|
break;
|
|
case WOAL_FROYO_WL_FW_RELOAD:
|
|
break;
|
|
case WOAL_FROYO_STOP:
|
|
if (IS_UAP_WEXT(priv->phandle->params.cfg80211_wext) &&
|
|
MLAN_STATUS_SUCCESS !=
|
|
woal_disconnect(priv, MOAL_IOCTL_WAIT, NULL,
|
|
DEF_DEAUTH_REASON_CODE)) {
|
|
ret = -EFAULT;
|
|
}
|
|
break;
|
|
#endif
|
|
default:
|
|
ret = -EINVAL;
|
|
break;
|
|
}
|
|
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @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/MLAN_STATUS_PENDING -- success,
|
|
* otherwise fail
|
|
*/
|
|
mlan_status woal_get_data_rates(moal_private *priv, t_u8 wait_option,
|
|
moal_802_11_rates *m_rates)
|
|
{
|
|
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) {
|
|
status = MLAN_STATUS_FAILURE;
|
|
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 (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/MLAN_STATUS_PENDING -- success,
|
|
* otherwise fail
|
|
*/
|
|
mlan_status woal_get_channel_list(moal_private *priv, t_u8 wait_option,
|
|
mlan_chan_list *chan_list)
|
|
{
|
|
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) {
|
|
status = MLAN_STATUS_FAILURE;
|
|
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) {
|
|
moal_memcpy_ext(priv->phandle, chan_list,
|
|
&bss->param.chanlist,
|
|
sizeof(mlan_chan_list),
|
|
sizeof(mlan_chan_list));
|
|
}
|
|
}
|
|
done:
|
|
if (status != MLAN_STATUS_PENDING)
|
|
kfree(req);
|
|
LEAVE();
|
|
return status;
|
|
}
|
|
|
|
/**
|
|
* @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;
|
|
}
|