summaryrefslogtreecommitdiff
path: root/drivers/net/wireless/sd8797/mlinux/moal_priv.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/sd8797/mlinux/moal_priv.c')
-rw-r--r--drivers/net/wireless/sd8797/mlinux/moal_priv.c6596
1 files changed, 6596 insertions, 0 deletions
diff --git a/drivers/net/wireless/sd8797/mlinux/moal_priv.c b/drivers/net/wireless/sd8797/mlinux/moal_priv.c
new file mode 100644
index 000000000000..dc5cd82b7f44
--- /dev/null
+++ b/drivers/net/wireless/sd8797/mlinux/moal_priv.c
@@ -0,0 +1,6596 @@
+/** @file moal_priv.c
+ *
+ * @brief This file contains standard ioctl functions
+ *
+ * Copyright (C) 2008-2011, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License"). You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available by writing to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
+ * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about
+ * this warranty disclaimer.
+ *
+ */
+
+/************************************************************************
+Change log:
+ 10/30/2008: initial version
+************************************************************************/
+
+#include "moal_main.h"
+#include "moal_sdio.h"
+
+#if defined(STA_CFG80211) && defined(UAP_CFG80211)
+#include "moal_cfg80211.h"
+#endif
+#include "moal_eth_ioctl.h"
+
+/********************************************************
+ Local Variables
+********************************************************/
+/** Bands supported in Infra mode */
+static t_u8 SupportedInfraBand[] = {
+ BAND_B,
+ BAND_B | BAND_G, BAND_G,
+ BAND_GN, BAND_B | BAND_G | BAND_GN, BAND_G | BAND_GN,
+ BAND_A, BAND_B | BAND_A, BAND_B | BAND_G | BAND_A, BAND_G | BAND_A,
+ BAND_A | BAND_B | BAND_G | BAND_AN | BAND_GN,
+ BAND_A | BAND_G | BAND_AN | BAND_GN, BAND_A | BAND_AN,
+};
+
+/** Bands supported in Ad-Hoc mode */
+static t_u8 SupportedAdhocBand[] = {
+ BAND_B, BAND_B | BAND_G, BAND_G,
+ BAND_GN, BAND_B | BAND_G | BAND_GN, BAND_G | BAND_GN,
+ BAND_A,
+ BAND_AN, BAND_A | BAND_AN,
+};
+
+/********************************************************
+ Global Variables
+********************************************************/
+
+extern int cfg80211_wext;
+
+/********************************************************
+ Local Functions
+********************************************************/
+
+/**
+ * @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;
+ int intf_num;
+ moal_handle *handle = priv->phandle;
+ mlan_ioctl_req *req = NULL;
+ mlan_ds_misc_cfg *misc = NULL;
+
+ ENTER();
+
+ woal_cancel_cac_block(priv);
+
+ /* Reset all interfaces */
+ ret = woal_reset_intf(priv, MOAL_IOCTL_WAIT, MTRUE);
+
+ /* Initialize private structures */
+ for (intf_num = 0; intf_num < handle->priv_num; intf_num++)
+ woal_init_priv(handle->priv[intf_num], MOAL_IOCTL_WAIT);
+
+ /* Restart the firmware */
+ req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg));
+ if (req) {
+ misc = (mlan_ds_misc_cfg *) req->pbuf;
+ misc->sub_command = MLAN_OID_MISC_WARM_RESET;
+ req->req_id = MLAN_IOCTL_MISC_CFG;
+ req->action = MLAN_ACT_SET;
+ if (MLAN_STATUS_SUCCESS !=
+ woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
+ ret = -EFAULT;
+ kfree(req);
+ goto done;
+ }
+ kfree(req);
+ }
+
+ /* Enable interfaces */
+ for (intf_num = 0; intf_num < handle->priv_num; intf_num++) {
+ netif_device_attach(handle->priv[intf_num]->netdev);
+ woal_start_queue(handle->priv[intf_num]->netdev);
+ }
+
+ done:
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief Get signal
+ *
+ * @param priv A pointer to moal_private structure
+ * @param wrq A pointer to iwreq structure
+ *
+ * @return 0 --success, otherwise fail
+ */
+static int
+woal_get_signal(moal_private * priv, struct iwreq *wrq)
+{
+/** Input data size */
+#define IN_DATA_SIZE 2
+/** Output data size */
+#define OUT_DATA_SIZE 12
+ int ret = 0;
+ int in_data[IN_DATA_SIZE];
+ int out_data[OUT_DATA_SIZE];
+ mlan_ds_get_signal signal;
+ int data_length = 0;
+ 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 Deep Sleep
+ *
+ * @param priv Pointer to the moal_private driver data struct
+ * @param deep_sleep Pointer to return deep_sleep setting
+ *
+ * @return 0 --success, otherwise fail
+ */
+static int
+woal_get_deep_sleep(moal_private * priv, t_u32 * data)
+{
+ int ret = 0;
+ mlan_ioctl_req *req = NULL;
+ mlan_ds_pm_cfg *pm = NULL;
+
+ ENTER();
+
+ req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_pm_cfg));
+ if (req == NULL) {
+ LEAVE();
+ return -ENOMEM;
+ }
+ pm = (mlan_ds_pm_cfg *) req->pbuf;
+ pm->sub_command = MLAN_OID_PM_CFG_DEEP_SLEEP;
+ req->req_id = MLAN_IOCTL_PM_CFG;
+
+ req->action = MLAN_ACT_GET;
+ if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
+ ret = -EFAULT;
+ goto done;
+ }
+ *data = pm->param.auto_deep_sleep.auto_ds;
+ *(data + 1) = pm->param.auto_deep_sleep.idletime;
+
+ done:
+ if (req)
+ kfree(req);
+
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief Get/Set DeepSleep mode
+ *
+ * @param priv Pointer to the moal_private driver data struct
+ * @param wreq A pointer to iwreq structure
+ *
+ * @return 0 --success, otherwise fail
+ */
+static int
+woal_deep_sleep_ioctl(moal_private * priv, struct iwreq *wrq)
+{
+ int ret = 0;
+ t_u32 deep_sleep = DEEP_SLEEP_OFF;
+ t_u32 data[2];
+ t_u16 idletime = DEEP_SLEEP_IDLE_TIME;
+
+ ENTER();
+
+ if (wrq->u.data.length == 1 || wrq->u.data.length == 2) {
+ if (copy_from_user
+ (&data, wrq->u.data.pointer, wrq->u.data.length * sizeof(int))) {
+ PRINTM(MERROR, "Copy from user failed\n");
+ LEAVE();
+ return -EFAULT;
+ }
+ deep_sleep = data[0];
+ if (deep_sleep == DEEP_SLEEP_OFF) {
+ PRINTM(MINFO, "Exit Deep Sleep Mode\n");
+ 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 (wrq->u.data.length == 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 (wrq->u.data.length > 2) {
+ PRINTM(MERROR, "Invalid number of arguments %d\n", wrq->u.data.length);
+ LEAVE();
+ return -EINVAL;
+ } else { /* Display Deep Sleep settings */
+ PRINTM(MINFO, "Get Deep Sleep Mode\n");
+ if (MLAN_STATUS_SUCCESS != woal_get_deep_sleep(priv, 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];
+ mlan_ioctl_req *req = NULL;
+ mlan_ds_11n_cfg *cfg_11n = NULL;
+ int ret = 0;
+ int data_length = wrq->u.data.length;
+
+ ENTER();
+
+ if (data_length > 2) {
+ PRINTM(MERROR, "Invalid number of arguments\n");
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (((req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg))) == NULL)) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ cfg_11n = (mlan_ds_11n_cfg *) req->pbuf;
+ cfg_11n->sub_command = MLAN_OID_11N_HTCAP_CFG;
+ req->req_id = MLAN_IOCTL_11N_CFG;
+
+ if (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, data_length * sizeof(int))) {
+ 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;
+ }
+
+ if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
+ 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;
+ if (MLAN_STATUS_SUCCESS !=
+ woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
+ ret = -EFAULT;
+ goto done;
+ }
+ if (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 (req)
+ kfree(req);
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief Enable/Disable amsdu_aggr_ctrl
+ *
+ * @param priv Pointer to the moal_private driver data struct
+ * @param wrq A pointer to iwreq structure
+ *
+ * @return 0 --success, otherwise fail
+ */
+static int
+woal_11n_amsdu_aggr_ctrl(moal_private * priv, struct iwreq *wrq)
+{
+ int data[2];
+ mlan_ioctl_req *req = NULL;
+ mlan_ds_11n_cfg *cfg_11n = NULL;
+ int ret = 0;
+
+ ENTER();
+
+ if ((wrq->u.data.length != 0) && (wrq->u.data.length != 1)) {
+ PRINTM(MERROR, "Invalid number of arguments\n");
+ ret = -EINVAL;
+ goto done;
+ }
+ req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg));
+ if (req == NULL) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ cfg_11n = (mlan_ds_11n_cfg *) req->pbuf;
+ cfg_11n->sub_command = MLAN_OID_11N_CFG_AMSDU_AGGR_CTRL;
+ req->req_id = MLAN_IOCTL_11N_CFG;
+
+ if (wrq->u.data.length == 0) {
+ /* Get 11n tx parameters from MLAN */
+ req->action = MLAN_ACT_GET;
+ } else if (wrq->u.data.length == 1) {
+ if (copy_from_user(data, wrq->u.data.pointer,
+ wrq->u.data.length * sizeof(int))) {
+ PRINTM(MERROR, "Copy from user failed\n");
+ ret = -EFAULT;
+ goto done;
+ }
+ cfg_11n->param.amsdu_aggr_ctrl.enable = data[0];
+ /* Update 11n tx parameters in MLAN */
+ req->action = MLAN_ACT_SET;
+ }
+ if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
+ ret = -EFAULT;
+ goto done;
+ }
+ data[0] = cfg_11n->param.amsdu_aggr_ctrl.enable;
+ data[1] = cfg_11n->param.amsdu_aggr_ctrl.curr_buf_size;
+
+ if (copy_to_user(wrq->u.data.pointer, data, sizeof(data))) {
+ PRINTM(MERROR, "Copy to user failed\n");
+ ret = -EFAULT;
+ goto done;
+ }
+ wrq->u.data.length = 2;
+ done:
+ if (req)
+ kfree(req);
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief Set/Get 11n configuration request
+ *
+ * @param priv Pointer to the moal_private driver data struct
+ * @param wrq A pointer to iwreq structure
+ *
+ * @return 0 --success, otherwise fail
+ */
+static int
+woal_11n_tx_cfg(moal_private * priv, struct iwreq *wrq)
+{
+ int data[2];
+ mlan_ioctl_req *req = NULL;
+ mlan_ds_11n_cfg *cfg_11n = NULL;
+ int ret = 0;
+ int data_length = wrq->u.data.length;
+
+ 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_CFG_TX;
+ req->req_id = MLAN_IOCTL_11N_CFG;
+
+ if (wrq->u.data.length == 0) {
+ /* Get 11n tx parameters from MLAN */
+ req->action = MLAN_ACT_GET;
+ cfg_11n->param.tx_cfg.misc_cfg = BAND_SELECT_BG;
+ } else {
+ if (copy_from_user
+ (data, wrq->u.data.pointer, data_length * sizeof(int))) {
+ 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;
+ }
+ if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
+ 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;
+ if (MLAN_STATUS_SUCCESS !=
+ woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
+ 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 (req)
+ kfree(req);
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief Enable/Disable TX Aggregation
+ *
+ * @param priv Pointer to the moal_private driver data struct
+ * @param wrq A pointer to iwreq structure
+ *
+ * @return 0 --success, otherwise fail
+ */
+static int
+woal_11n_prio_tbl(moal_private * priv, struct iwreq *wrq)
+{
+ int data[MAX_NUM_TID * 2], i, j;
+ mlan_ioctl_req *req = NULL;
+ mlan_ds_11n_cfg *cfg_11n = NULL;
+ int ret = 0;
+
+ ENTER();
+
+ if ((wrq->u.data.pointer == NULL)) {
+ LEAVE();
+ return -EINVAL;
+ }
+
+ req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg));
+ if (req == NULL) {
+ LEAVE();
+ return -ENOMEM;
+ }
+ cfg_11n = (mlan_ds_11n_cfg *) req->pbuf;
+ cfg_11n->sub_command = MLAN_OID_11N_CFG_AGGR_PRIO_TBL;
+ req->req_id = MLAN_IOCTL_11N_CFG;
+
+ if (wrq->u.data.length == 0) {
+ /* Get aggr priority table from MLAN */
+ req->action = MLAN_ACT_GET;
+ if (MLAN_STATUS_SUCCESS !=
+ woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
+ ret = -EFAULT;
+ goto error;
+ }
+ wrq->u.data.length = MAX_NUM_TID * 2;
+ for (i = 0, j = 0; i < (wrq->u.data.length); i = i + 2, ++j) {
+ data[i] = cfg_11n->param.aggr_prio_tbl.ampdu[j];
+ data[i + 1] = cfg_11n->param.aggr_prio_tbl.amsdu[j];
+ }
+
+ if (copy_to_user(wrq->u.data.pointer, data,
+ sizeof(int) * wrq->u.data.length)) {
+ PRINTM(MERROR, "Copy to user failed\n");
+ ret = -EFAULT;
+ goto error;
+ }
+ } else if (wrq->u.data.length == 16) {
+ if (copy_from_user(data, wrq->u.data.pointer,
+ sizeof(int) * wrq->u.data.length)) {
+ PRINTM(MERROR, "Copy from user failed\n");
+ ret = -EFAULT;
+ goto error;
+ }
+ for (i = 0, j = 0; i < (wrq->u.data.length); i = i + 2, ++j) {
+ 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;
+ if (MLAN_STATUS_SUCCESS !=
+ woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
+ ret = -EFAULT;
+ goto error;
+ }
+ } else {
+ PRINTM(MERROR, "Invalid number of arguments\n");
+ ret = -EINVAL;
+ goto error;
+ }
+
+ error:
+ if (req)
+ kfree(req);
+
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief Set/Get add BA Reject 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;
+ mlan_ioctl_req *req = NULL;
+ mlan_ds_11n_cfg *cfg_11n = NULL;
+
+ ENTER();
+
+ req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg));
+ if (req == NULL) {
+ LEAVE();
+ return -ENOMEM;
+ }
+ cfg_11n = (mlan_ds_11n_cfg *) req->pbuf;
+ cfg_11n->sub_command = MLAN_OID_11N_CFG_ADDBA_REJECT;
+ req->req_id = MLAN_IOCTL_11N_CFG;
+
+ if (wrq->u.data.length == 0) {
+ PRINTM(MERROR, "Addba reject moal\n");
+ /* Get aggr priority table from MLAN */
+ req->action = MLAN_ACT_GET;
+ if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req,
+ MOAL_IOCTL_WAIT)) {
+ ret = -EFAULT;
+ goto error;
+ }
+
+ wrq->u.data.length = MAX_NUM_TID;
+ for (i = 0; i < (wrq->u.data.length); ++i) {
+ data[i] = cfg_11n->param.addba_reject[i];
+ }
+
+ if (copy_to_user(wrq->u.data.pointer, data,
+ sizeof(int) * wrq->u.data.length)) {
+ PRINTM(MERROR, "Copy to user failed\n");
+ ret = -EFAULT;
+ goto error;
+ }
+ } else if (wrq->u.data.length == 8) {
+ if (copy_from_user(data, wrq->u.data.pointer,
+ sizeof(int) * wrq->u.data.length)) {
+ PRINTM(MERROR, "Copy from user failed\n");
+ ret = -EFAULT;
+ goto error;
+ }
+ for (i = 0; i < (wrq->u.data.length); ++i) {
+ 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;
+ if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req,
+ MOAL_IOCTL_WAIT)) {
+ ret = -EFAULT;
+ goto error;
+ }
+ } else {
+ PRINTM(MERROR, "Invalid number of arguments\n");
+ ret = -EINVAL;
+ goto error;
+ }
+ error:
+ if (req)
+ kfree(req);
+
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief Set/Get add BA 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;
+ mlan_ioctl_req *req = NULL;
+ mlan_ds_11n_cfg *cfg_11n = NULL;
+
+ ENTER();
+
+ req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg));
+ if (req == NULL) {
+ LEAVE();
+ return -ENOMEM;
+ }
+ cfg_11n = (mlan_ds_11n_cfg *) req->pbuf;
+ cfg_11n->sub_command = MLAN_OID_11N_CFG_ADDBA_PARAM;
+ req->req_id = MLAN_IOCTL_11N_CFG;
+
+ if (wrq->u.data.length == 0) {
+ /* Get Add BA parameters from MLAN */
+ req->action = MLAN_ACT_GET;
+ if (MLAN_STATUS_SUCCESS !=
+ woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
+ ret = -EFAULT;
+ goto error;
+ }
+ data[0] = cfg_11n->param.addba_param.timeout;
+ data[1] = cfg_11n->param.addba_param.txwinsize;
+ data[2] = cfg_11n->param.addba_param.rxwinsize;
+ 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 (wrq->u.data.length == 5) {
+ if (copy_from_user
+ (data, wrq->u.data.pointer, wrq->u.data.length * sizeof(int))) {
+ PRINTM(MERROR, "Copy from user failed\n");
+ ret = -EFAULT;
+ goto error;
+ }
+ 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;
+ if (MLAN_STATUS_SUCCESS !=
+ woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
+ ret = MLAN_STATUS_FAILURE;
+ goto error;
+ }
+ } else {
+ PRINTM(MERROR, "Invalid number of arguments\n");
+ ret = -EINVAL;
+ goto error;
+ }
+
+ error:
+ if (req)
+ kfree(req);
+
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief Set/Get Transmit buffer size
+ *
+ * @param priv Pointer to the moal_private driver data struct
+ * @param wrq A pointer to iwreq structure
+ *
+ * @return 0 --success, otherwise fail
+ */
+static int
+woal_txbuf_cfg(moal_private * priv, struct iwreq *wrq)
+{
+ int buf_size;
+ mlan_ioctl_req *req = NULL;
+ mlan_ds_11n_cfg *cfg_11n = NULL;
+ int ret = 0;
+
+ ENTER();
+ req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg));
+ if (req == NULL) {
+ ret = -ENOMEM;
+ goto done;
+ }
+ cfg_11n = (mlan_ds_11n_cfg *) req->pbuf;
+ cfg_11n->sub_command = MLAN_OID_11N_CFG_MAX_TX_BUF_SIZE;
+ req->req_id = MLAN_IOCTL_11N_CFG;
+
+ if (wrq->u.data.length == 0) {
+ /* Get Tx buffer size from MLAN */
+ req->action = MLAN_ACT_GET;
+ } else {
+ ret = -EINVAL;
+ PRINTM(MERROR,
+ "Don't support set Tx buffer size after driver loaded!\n");
+ goto done;
+ }
+ if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
+ ret = -EFAULT;
+ goto done;
+ }
+ buf_size = cfg_11n->param.tx_buf_size;
+ if (copy_to_user(wrq->u.data.pointer, &buf_size, sizeof(buf_size))) {
+ PRINTM(MERROR, "Copy to user failed\n");
+ ret = -EFAULT;
+ goto done;
+ }
+ wrq->u.data.length = 1;
+ done:
+ if (req)
+ kfree(req);
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief Set/Get Host Sleep configuration
+ *
+ * @param priv A pointer to moal_private structure
+ * @param wrq A pointer to iwreq structure
+ * @param invoke_hostcmd MTRUE --invoke HostCmd, otherwise MFALSE
+ *
+ * @return 0 --success, otherwise fail
+ */
+static int
+woal_hs_cfg(moal_private * priv, struct iwreq *wrq, BOOLEAN invoke_hostcmd)
+{
+ int data[3];
+ int ret = 0;
+ mlan_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));
+
+ 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, sizeof(int) * data_length)) {
+ PRINTM(MERROR, "Copy from user failed\n");
+ ret = -EFAULT;
+ goto done;
+ }
+ } else {
+ PRINTM(MERROR, "Invalid arguments\n");
+ ret = -EINVAL;
+ goto done;
+ }
+ }
+
+ /* HS config is blocked if HS is already activated */
+ if (data_length &&
+ (data[0] != 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];
+ int ret = 0;
+ mlan_ioctl_req *req = NULL;
+ mlan_ds_pm_cfg *pmcfg = NULL;
+ pmlan_ds_inactivity_to inac_to = NULL;
+ int data_length = wrq->u.data.length;
+
+ ENTER();
+
+ memset(data, 0, sizeof(data));
+ req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_pm_cfg));
+ if (req == NULL) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ pmcfg = (mlan_ds_pm_cfg *) req->pbuf;
+ inac_to = &pmcfg->param.inactivity_to;
+ pmcfg->sub_command = MLAN_OID_PM_CFG_INACTIVITY_TO;
+ req->req_id = MLAN_IOCTL_PM_CFG;
+
+ if ((data_length != 0 && data_length != 3 && data_length != 4) ||
+ 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, sizeof(int) * data_length)) {
+ PRINTM(MERROR, "Copy from user failed\n");
+ ret = -EFAULT;
+ goto done;
+ }
+ req->action = MLAN_ACT_SET;
+ inac_to->timeout_unit = data[0];
+ inac_to->unicast_timeout = data[1];
+ inac_to->mcast_timeout = data[2];
+ inac_to->ps_entry_timeout = data[3];
+ }
+
+ if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
+ ret = -EFAULT;
+ goto done;
+ }
+
+ /* Copy back current values regardless of GET/SET */
+ data[0] = inac_to->timeout_unit;
+ data[1] = inac_to->unicast_timeout;
+ data[2] = inac_to->mcast_timeout;
+ data[3] = inac_to->ps_entry_timeout;
+
+ if (req->action == MLAN_ACT_GET) {
+ wrq->u.data.length = 4;
+ if (copy_to_user
+ (wrq->u.data.pointer, data, wrq->u.data.length * sizeof(int))) {
+ PRINTM(MERROR, "Copy to user failed\n");
+ ret = -EFAULT;
+ goto done;
+ }
+ }
+
+ done:
+ if (req)
+ kfree(req);
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief Set/Get system clock
+ *
+ * @param priv A pointer to moal_private structure
+ * @param wrq A pointer to iwreq structure
+ *
+ * @return 0 --success, otherwise fail
+ */
+static int
+woal_ecl_sys_clock(moal_private * priv, struct iwreq *wrq)
+{
+ int data[64];
+ int ret = 0;
+ mlan_ioctl_req *req = NULL;
+ mlan_ds_misc_cfg *cfg = NULL;
+ int data_length = wrq->u.data.length;
+ int i = 0;
+
+ ENTER();
+
+ memset(data, 0, sizeof(data));
+
+ req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg));
+ if (req == NULL) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ cfg = (mlan_ds_misc_cfg *) req->pbuf;
+ cfg->sub_command = MLAN_OID_MISC_SYS_CLOCK;
+ req->req_id = MLAN_IOCTL_MISC_CFG;
+
+ if (!data_length)
+ req->action = MLAN_ACT_GET;
+ else if (data_length <= MLAN_MAX_CLK_NUM) {
+ req->action = MLAN_ACT_SET;
+ if (copy_from_user
+ (data, wrq->u.data.pointer, sizeof(int) * data_length)) {
+ PRINTM(MERROR, "Copy from user failed\n");
+ ret = -EFAULT;
+ goto done;
+ }
+ } else {
+ PRINTM(MERROR, "Invalid arguments\n");
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (req->action == MLAN_ACT_GET) {
+ /* Get configurable clocks */
+ cfg->param.sys_clock.sys_clk_type = MLAN_CLK_CONFIGURABLE;
+ if (MLAN_STATUS_SUCCESS !=
+ woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
+ ret = -EFAULT;
+ goto done;
+ }
+
+ /* Current system clock */
+ data[0] = (int) cfg->param.sys_clock.cur_sys_clk;
+ wrq->u.data.length = 1;
+
+ data_length = MIN(cfg->param.sys_clock.sys_clk_num, MLAN_MAX_CLK_NUM);
+
+ /* Configurable clocks */
+ for (i = 0; i < data_length; i++) {
+ data[i + wrq->u.data.length] =
+ (int) cfg->param.sys_clock.sys_clk[i];
+ }
+ wrq->u.data.length += data_length;
+
+ /* Get supported clocks */
+ cfg->param.sys_clock.sys_clk_type = MLAN_CLK_SUPPORTED;
+ if (MLAN_STATUS_SUCCESS !=
+ woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
+ ret = -EFAULT;
+ goto done;
+ }
+
+ data_length = MIN(cfg->param.sys_clock.sys_clk_num, MLAN_MAX_CLK_NUM);
+
+ /* Supported clocks */
+ for (i = 0; i < data_length; i++) {
+ data[i + wrq->u.data.length] =
+ (int) cfg->param.sys_clock.sys_clk[i];
+ }
+
+ wrq->u.data.length += data_length;
+
+ if (copy_to_user
+ (wrq->u.data.pointer, data, sizeof(int) * wrq->u.data.length)) {
+ PRINTM(MERROR, "Copy to user failed\n");
+ ret = -EFAULT;
+ goto done;
+ }
+ } else {
+ /* Set configurable clocks */
+ cfg->param.sys_clock.sys_clk_type = MLAN_CLK_CONFIGURABLE;
+ cfg->param.sys_clock.sys_clk_num = MIN(MLAN_MAX_CLK_NUM, data_length);
+ for (i = 0; i < cfg->param.sys_clock.sys_clk_num; i++) {
+ cfg->param.sys_clock.sys_clk[i] = (t_u16) data[i];
+ }
+
+ if (MLAN_STATUS_SUCCESS !=
+ woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
+ ret = -EFAULT;
+ goto done;
+ }
+ }
+ done:
+ if (req)
+ kfree(req);
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief Set/Get Band and Adhoc-band setting
+ *
+ * @param priv A pointer to moal_private structure
+ * @param wrq A pointer to iwreq structure
+ *
+ * @return 0 --success, otherwise fail
+ */
+static int
+woal_band_cfg(moal_private * priv, struct iwreq *wrq)
+{
+ int ret = 0;
+ unsigned int i;
+ int data[4];
+ int user_data_len = wrq->u.data.length;
+ t_u32 infra_band = 0;
+ t_u32 adhoc_band = 0;
+ t_u32 adhoc_channel = 0;
+ t_u32 adhoc_chan_bandwidth = 0;
+ mlan_ioctl_req *req = NULL;
+ mlan_ds_radio_cfg *radio_cfg = NULL;
+
+ 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;
+ }
+ }
+
+ 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;
+ if (MLAN_STATUS_SUCCESS !=
+ woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
+ ret = -EFAULT;
+ goto error;
+ }
+ data[0] = radio_cfg->param.band_cfg.config_bands; /* Infra Band */
+ data[1] = radio_cfg->param.band_cfg.adhoc_start_band; /* Adhoc Band */
+ data[2] = radio_cfg->param.band_cfg.adhoc_channel; /* Adhoc
+ Channel */
+ wrq->u.data.length = 3;
+ if (radio_cfg->param.band_cfg.adhoc_start_band & BAND_GN
+ || radio_cfg->param.band_cfg.adhoc_start_band & BAND_AN) {
+ data[3] = radio_cfg->param.band_cfg.sec_chan_offset;
+ wrq->u.data.length = 4;
+ }
+
+ if (copy_to_user
+ (wrq->u.data.pointer, data, sizeof(int) * wrq->u.data.length)) {
+ ret = -EFAULT;
+ goto error;
+ }
+ } else {
+ if (copy_from_user
+ (data, wrq->u.data.pointer, sizeof(int) * user_data_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;
+ }
+ }
+ if (user_data_len == 4) {
+ if (!(adhoc_band & (BAND_GN | BAND_AN))) {
+ PRINTM(MERROR,
+ "11n is not enabled for adhoc, can not set HT/VHT channel bandwidth\n");
+ ret = -EINVAL;
+ goto error;
+ }
+ adhoc_chan_bandwidth = data[3];
+ if ((adhoc_chan_bandwidth != CHANNEL_BW_20MHZ) &&
+ (adhoc_chan_bandwidth != CHANNEL_BW_40MHZ_ABOVE) &&
+ (adhoc_chan_bandwidth != CHANNEL_BW_40MHZ_BELOW)
+ ) {
+ PRINTM(MERROR,
+ "Invalid secondary channel bandwidth, only allowed 0, 1, 3 or 4\n");
+ 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;
+ radio_cfg->param.band_cfg.sec_chan_offset = adhoc_chan_bandwidth;
+ if (MLAN_STATUS_SUCCESS !=
+ woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
+ ret = -EFAULT;
+ goto error;
+ }
+ }
+
+ error:
+ if (req)
+ kfree(req);
+
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief Read/Write adapter registers value
+ *
+ * @param priv A pointer to moal_private structure
+ * @param wrq A pointer to iwreq structure
+ *
+ * @return 0 --success, otherwise fail
+ */
+static int
+woal_reg_read_write(moal_private * priv, struct iwreq *wrq)
+{
+ int data[3];
+ int ret = 0;
+ mlan_ioctl_req *req = NULL;
+ mlan_ds_reg_mem *reg = NULL;
+ int data_length = wrq->u.data.length;
+
+ ENTER();
+
+ memset(data, 0, sizeof(data));
+
+ req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_reg_mem));
+ if (req == NULL) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ reg = (mlan_ds_reg_mem *) req->pbuf;
+ reg->sub_command = MLAN_OID_REG_RW;
+ req->req_id = MLAN_IOCTL_REG_MEM;
+
+ if (data_length == 2) {
+ req->action = MLAN_ACT_GET;
+ } else if (data_length == 3) {
+ req->action = MLAN_ACT_SET;
+ } else {
+ ret = -EINVAL;
+ goto done;
+ }
+ if (copy_from_user(data, wrq->u.data.pointer, sizeof(int) * data_length)) {
+ PRINTM(MERROR, "Copy from user failed\n");
+ ret = -EFAULT;
+ goto done;
+ }
+ reg->param.reg_rw.type = (t_u32) data[0];
+ reg->param.reg_rw.offset = (t_u32) data[1];
+ if (data_length == 3)
+ reg->param.reg_rw.value = (t_u32) data[2];
+
+ if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
+ ret = -EFAULT;
+ goto done;
+ }
+
+ if (req->action == MLAN_ACT_GET) {
+ if (copy_to_user
+ (wrq->u.data.pointer, &reg->param.reg_rw.value, sizeof(int))) {
+ PRINTM(MERROR, "Copy to user failed\n");
+ ret = -EFAULT;
+ goto done;
+ }
+ wrq->u.data.length = 1;
+ }
+
+ done:
+ if (req)
+ kfree(req);
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief Read the EEPROM contents of the card
+ *
+ * @param priv A pointer to moal_private structure
+ * @param wrq A pointer to iwreq structure
+ *
+ * @return 0 --success, otherwise fail
+ */
+static int
+woal_read_eeprom(moal_private * priv, struct iwreq *wrq)
+{
+ int data[2];
+ int ret = 0;
+ mlan_ioctl_req *req = NULL;
+ mlan_ds_reg_mem *reg = NULL;
+ int data_length = wrq->u.data.length;
+
+ ENTER();
+
+ memset(data, 0, sizeof(data));
+
+ req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_reg_mem));
+ if (req == NULL) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ reg = (mlan_ds_reg_mem *) req->pbuf;
+ reg->sub_command = MLAN_OID_EEPROM_RD;
+ req->req_id = MLAN_IOCTL_REG_MEM;
+
+ if (data_length == 2) {
+ req->action = MLAN_ACT_GET;
+ } else {
+ ret = -EINVAL;
+ goto done;
+ }
+ if (copy_from_user(data, wrq->u.data.pointer, sizeof(int) * data_length)) {
+ PRINTM(MERROR, "Copy from user failed\n");
+ ret = -EFAULT;
+ goto done;
+ }
+
+ reg->param.rd_eeprom.offset = (t_u16) data[0];
+ reg->param.rd_eeprom.byte_count = (t_u16) data[1];
+
+ if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
+ ret = -EFAULT;
+ goto done;
+ }
+
+ if (req->action == MLAN_ACT_GET) {
+ wrq->u.data.length = reg->param.rd_eeprom.byte_count;
+ if (copy_to_user
+ (wrq->u.data.pointer, reg->param.rd_eeprom.value,
+ MIN(wrq->u.data.length, MAX_EEPROM_DATA))) {
+ PRINTM(MERROR, "Copy to user failed\n");
+ ret = -EFAULT;
+ goto done;
+ }
+ }
+
+ done:
+ if (req)
+ kfree(req);
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief Read/Write device memory value
+ *
+ * @param priv A pointer to moal_private structure
+ * @param wrq A pointer to iwreq structure
+ *
+ * @return 0 --success, otherwise fail
+ */
+static int
+woal_mem_read_write(moal_private * priv, struct iwreq *wrq)
+{
+ t_u32 data[2];
+ int ret = 0;
+ mlan_ioctl_req *req = NULL;
+ mlan_ds_reg_mem *reg_mem = NULL;
+ int data_length = wrq->u.data.length;
+
+ ENTER();
+
+ memset(data, 0, sizeof(data));
+
+ req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_reg_mem));
+ if (req == NULL) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ reg_mem = (mlan_ds_reg_mem *) req->pbuf;
+ reg_mem->sub_command = MLAN_OID_MEM_RW;
+ req->req_id = MLAN_IOCTL_REG_MEM;
+
+ if (data_length == 1) {
+ PRINTM(MINFO, "MEM_RW: GET\n");
+ req->action = MLAN_ACT_GET;
+ } else if (data_length == 2) {
+ PRINTM(MINFO, "MEM_RW: SET\n");
+ req->action = MLAN_ACT_SET;
+ } else {
+ ret = -EINVAL;
+ goto done;
+ }
+ if (copy_from_user(data, wrq->u.data.pointer, sizeof(int) * data_length)) {
+ PRINTM(MERROR, "Copy from user failed\n");
+ ret = -EFAULT;
+ goto done;
+ }
+
+ reg_mem->param.mem_rw.addr = (t_u32) data[0];
+ if (data_length == 2)
+ reg_mem->param.mem_rw.value = (t_u32) data[1];
+
+ PRINTM(MINFO, "MEM_RW: Addr=0x%x, Value=0x%x\n",
+ (int) reg_mem->param.mem_rw.addr, (int) reg_mem->param.mem_rw.value);
+
+ if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
+ ret = -EFAULT;
+ goto done;
+ }
+
+ if (req->action == MLAN_ACT_GET) {
+ if (copy_to_user
+ (wrq->u.data.pointer, &reg_mem->param.mem_rw.value, sizeof(int))) {
+ PRINTM(MERROR, "Copy to user failed\n");
+ ret = -EFAULT;
+ goto done;
+ }
+ wrq->u.data.length = 1;
+ }
+
+ done:
+ if (req)
+ kfree(req);
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief Get LOG
+ *
+ * @param priv A pointer to moal_private structure
+ * @param wrq A pointer to iwreq structure
+ *
+ * @return 0 --success, otherwise fail
+ */
+static int
+woal_get_log(moal_private * priv, struct iwreq *wrq)
+{
+ int ret = 0;
+ mlan_ds_get_stats stats;
+ char *buf = NULL;
+
+ ENTER();
+
+ PRINTM(MINFO, " GET STATS\n");
+ if (!(buf = kmalloc(GETLOG_BUFSIZE, GFP_KERNEL))) {
+ 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",
+ stats.mcast_tx_frame,
+ stats.failed,
+ stats.retry,
+ stats.multi_retry,
+ stats.frame_dup,
+ stats.rts_success,
+ stats.rts_failure,
+ stats.ack_failure,
+ stats.rx_frag,
+ stats.mcast_rx_frame,
+ stats.fcs_error,
+ stats.tx_frame,
+ stats.wep_icv_error[0],
+ stats.wep_icv_error[1],
+ stats.wep_icv_error[2], stats.wep_icv_error[3]);
+ wrq->u.data.length = strlen(buf) + 1;
+ if (copy_to_user(wrq->u.data.pointer, buf, wrq->u.data.length)) {
+ PRINTM(MERROR, "Copy to user failed\n");
+ ret = -EFAULT;
+ }
+ }
+ done:
+ if (buf)
+ kfree(buf);
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief Deauthenticate
+ *
+ * @param priv A pointer to moal_private structure
+ * @param wrq A pointer to iwreq structure
+ *
+ * @return 0 --success, otherwise fail
+ */
+static int
+woal_deauth(moal_private * priv, struct iwreq *wrq)
+{
+ int ret = 0;
+ struct sockaddr saddr;
+
+ ENTER();
+ if (wrq->u.data.length) {
+ /* Deauth mentioned BSSID */
+ if (copy_from_user(&saddr, wrq->u.data.pointer, sizeof(saddr))) {
+ PRINTM(MERROR, "Copy from user failed\n");
+ ret = -EFAULT;
+ goto done;
+ }
+ if (MLAN_STATUS_SUCCESS !=
+ woal_disconnect(priv, MOAL_IOCTL_WAIT, (t_u8 *) saddr.sa_data)) {
+ ret = -EFAULT;
+ goto done;
+ }
+ } else {
+ if (MLAN_STATUS_SUCCESS != woal_disconnect(priv, MOAL_IOCTL_WAIT, NULL))
+ ret = -EFAULT;
+ }
+ done:
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief Set/Get TX power configurations
+ *
+ * @param priv A pointer to moal_private structure
+ * @param wrq A pointer to iwreq structure
+ *
+ * @return 0 --success, otherwise fail
+ */
+static int
+woal_tx_power_cfg(moal_private * priv, struct iwreq *wrq)
+{
+ int data[5], user_data_len;
+ int ret = 0;
+ mlan_bss_info bss_info;
+ mlan_ds_power_cfg *pcfg = NULL;
+ mlan_ioctl_req *req = NULL;
+ ENTER();
+
+ memset(&bss_info, 0, sizeof(bss_info));
+ woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info);
+
+ memset(data, 0, sizeof(data));
+ user_data_len = wrq->u.data.length;
+
+ if (user_data_len) {
+ if (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, sizeof(int) * user_data_len)) {
+ PRINTM(MERROR, "Copy from user failed\n");
+ ret = -EFAULT;
+ goto done;
+ }
+ switch (user_data_len) {
+ case 1:
+ if (data[0] != 0xFF)
+ ret = -EINVAL;
+ break;
+ case 2:
+ case 4:
+ if (data[0] == 0xFF) {
+ ret = -EINVAL;
+ break;
+ }
+ if ((unsigned int) 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 ((unsigned int) data[2] > bss_info.max_power_level) {
+ PRINTM(MERROR,
+ "The set powercfg rate value %d dBm is out of range (%d dBm-%d dBm)!\n",
+ data[2], (int) bss_info.min_power_level,
+ (int) bss_info.max_power_level);
+ ret = -EINVAL;
+ break;
+ }
+ if (data[3] > data[2] - data[1]) {
+ PRINTM(MERROR,
+ "Step should not greater than power difference!\n");
+ ret = -EINVAL;
+ break;
+ }
+ }
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ if (ret)
+ goto done;
+ }
+
+ req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_power_cfg));
+ if (req == NULL) {
+ ret = -ENOMEM;
+ goto done;
+ }
+ pcfg = (mlan_ds_power_cfg *) req->pbuf;
+ pcfg->sub_command = MLAN_OID_POWER_CFG_EXT;
+ req->req_id = MLAN_IOCTL_POWER_CFG;
+ if (!user_data_len)
+ req->action = MLAN_ACT_GET;
+ else {
+ req->action = MLAN_ACT_SET;
+ pcfg->param.power_ext.len = user_data_len;
+ memcpy((t_u8 *) & pcfg->param.power_ext.power_data, (t_u8 *) data,
+ sizeof(data));
+ }
+ if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
+ ret = -EFAULT;
+ goto done;
+ }
+ if (!user_data_len) {
+ if (copy_to_user
+ (wrq->u.data.pointer, (t_u8 *) & pcfg->param.power_ext.power_data,
+ sizeof(int) * pcfg->param.power_ext.len)) {
+ ret = -EFAULT;
+ goto done;
+ }
+ wrq->u.data.length = pcfg->param.power_ext.len;
+ }
+ done:
+ if (req)
+ kfree(req);
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief Get Tx/Rx data rates
+ *
+ * @param priv A pointer to moal_private structure
+ * @param wrq A pointer to iwreq structure
+ *
+ * @return 0 --success, otherwise fail
+ */
+static int
+woal_get_txrx_rate(moal_private * priv, struct iwreq *wrq)
+{
+ int ret = 0;
+ mlan_ds_rate *rate = NULL;
+ mlan_ioctl_req *req = NULL;
+
+ ENTER();
+
+ req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_rate));
+ if (req == NULL) {
+ ret = -ENOMEM;
+ goto done;
+ }
+ rate = (mlan_ds_rate *) req->pbuf;
+ rate->sub_command = MLAN_OID_GET_DATA_RATE;
+ req->req_id = MLAN_IOCTL_RATE;
+ req->action = MLAN_ACT_GET;
+
+ if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
+ ret = -EFAULT;
+ goto done;
+ }
+
+ if (copy_to_user
+ (wrq->u.data.pointer, (t_u8 *) & rate->param.data_rate,
+ sizeof(int) * 2)) {
+ ret = -EFAULT;
+ goto done;
+ }
+ wrq->u.data.length = 2;
+ done:
+ if (req)
+ kfree(req);
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief Turn on/off the sdio clock
+ *
+ * @param priv A pointer to moal_private structure
+ * @param wrq A pointer to iwreq structure
+ *
+ * @return 0/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;
+}
+
+/**
+ * @brief Set/Get beacon interval
+ *
+ * @param priv A pointer to moal_private structure
+ * @param wrq A pointer to iwreq structure
+ *
+ * @return 0 --success, otherwise fail
+ */
+static int
+woal_beacon_interval(moal_private * priv, struct iwreq *wrq)
+{
+ int ret = 0;
+ mlan_ds_bss *bss = NULL;
+ mlan_ioctl_req *req = NULL;
+ int bcn = 0;
+
+ ENTER();
+
+ if (wrq->u.data.length) {
+ if (copy_from_user(&bcn, wrq->u.data.pointer, sizeof(int))) {
+ PRINTM(MERROR, "Copy from user failed\n");
+ ret = -EFAULT;
+ goto done;
+ }
+ if ((bcn < MLAN_MIN_BEACON_INTERVAL) ||
+ (bcn > MLAN_MAX_BEACON_INTERVAL)) {
+ ret = -EINVAL;
+ goto done;
+ }
+ }
+
+ req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss));
+ if (req == NULL) {
+ ret = -ENOMEM;
+ goto done;
+ }
+ bss = (mlan_ds_bss *) req->pbuf;
+ bss->sub_command = MLAN_OID_IBSS_BCN_INTERVAL;
+ req->req_id = MLAN_IOCTL_BSS;
+ if (!wrq->u.data.length)
+ req->action = MLAN_ACT_GET;
+ else {
+ req->action = MLAN_ACT_SET;
+ bss->param.bcn_interval = bcn;
+ }
+
+ if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
+ ret = -EFAULT;
+ goto done;
+ }
+
+ if (copy_to_user
+ (wrq->u.data.pointer, (t_u8 *) & bss->param.bcn_interval,
+ sizeof(int))) {
+ ret = -EFAULT;
+ goto done;
+ }
+ wrq->u.data.length = 1;
+ done:
+ if (req)
+ kfree(req);
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief Set/Get ATIM window
+ *
+ * @param priv A pointer to moal_private structure
+ * @param wrq A pointer to iwreq structure
+ *
+ * @return 0 --success, otherwise fail
+ */
+static int
+woal_atim_window(moal_private * priv, struct iwreq *wrq)
+{
+ int ret = 0;
+ mlan_ds_bss *bss = NULL;
+ mlan_ioctl_req *req = NULL;
+ int atim = 0;
+
+ ENTER();
+
+ if (wrq->u.data.length) {
+ if (copy_from_user(&atim, wrq->u.data.pointer, sizeof(int))) {
+ PRINTM(MERROR, "Copy from user failed\n");
+ ret = -EFAULT;
+ goto done;
+ }
+ if ((atim < 0) || (atim > MLAN_MAX_ATIM_WINDOW)) {
+ ret = -EINVAL;
+ goto done;
+ }
+ }
+
+ req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss));
+ if (req == NULL) {
+ ret = -ENOMEM;
+ goto done;
+ }
+ bss = (mlan_ds_bss *) req->pbuf;
+ bss->sub_command = MLAN_OID_IBSS_ATIM_WINDOW;
+ req->req_id = MLAN_IOCTL_BSS;
+ if (!wrq->u.data.length)
+ req->action = MLAN_ACT_GET;
+ else {
+ req->action = MLAN_ACT_SET;
+ bss->param.atim_window = atim;
+ }
+
+ if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
+ ret = -EFAULT;
+ goto done;
+ }
+
+ if (copy_to_user
+ (wrq->u.data.pointer, (t_u8 *) & bss->param.atim_window, sizeof(int))) {
+ ret = -EFAULT;
+ goto done;
+ }
+ wrq->u.data.length = 1;
+ done:
+ if (req)
+ kfree(req);
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief Set/Get TX data rate
+ *
+ * @param priv A pointer to moal_private structure
+ * @param wrq A pointer to iwreq structure
+ *
+ * @return 0 --success, otherwise fail
+ */
+static int
+woal_set_get_txrate(moal_private * priv, struct iwreq *wrq)
+{
+ int ret = 0;
+ mlan_ds_rate *rate = NULL;
+ mlan_ioctl_req *req = NULL;
+ int rateindex = 0;
+
+ ENTER();
+ if (wrq->u.data.length) {
+ if (copy_from_user(&rateindex, wrq->u.data.pointer, sizeof(int))) {
+ PRINTM(MERROR, "Copy from user failed\n");
+ ret = -EFAULT;
+ goto done;
+ }
+ }
+ req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_rate));
+ if (req == NULL) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ rate = (mlan_ds_rate *) req->pbuf;
+ rate->param.rate_cfg.rate_type = MLAN_RATE_INDEX;
+ rate->sub_command = MLAN_OID_RATE_CFG;
+ req->req_id = MLAN_IOCTL_RATE;
+ if (!wrq->u.data.length)
+ req->action = MLAN_ACT_GET;
+ else {
+ req->action = MLAN_ACT_SET;
+ if (rateindex == AUTO_RATE)
+ rate->param.rate_cfg.is_rate_auto = 1;
+ else {
+ if ((rateindex != MLAN_RATE_INDEX_MCS32) &&
+ ((rateindex < 0) || (rateindex > MLAN_RATE_INDEX_MCS15))) {
+ ret = -EINVAL;
+ goto done;
+ }
+ }
+ rate->param.rate_cfg.rate = rateindex;
+ }
+
+ if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
+ ret = -EFAULT;
+ goto done;
+ } else {
+ if (wrq->u.data.length)
+ priv->rate_index = rateindex;
+ }
+ if (!wrq->u.data.length) {
+ if (rate->param.rate_cfg.is_rate_auto)
+ rateindex = AUTO_RATE;
+ else
+ rateindex = rate->param.rate_cfg.rate;
+ wrq->u.data.length = 1;
+ if (copy_to_user(wrq->u.data.pointer, &rateindex, sizeof(int))) {
+ ret = -EFAULT;
+ }
+ }
+ done:
+ if (req)
+ kfree(req);
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief Set/Get region code
+ *
+ * @param priv A pointer to moal_private structure
+ * @param wrq A pointer to iwreq structure
+ *
+ * @return 0 --success, otherwise fail
+ */
+static int
+woal_set_get_regioncode(moal_private * priv, struct iwreq *wrq)
+{
+ int ret = 0;
+ mlan_ds_misc_cfg *cfg = NULL;
+ mlan_ioctl_req *req = NULL;
+ int region = 0;
+
+ ENTER();
+
+ if (wrq->u.data.length) {
+ if (copy_from_user(&region, wrq->u.data.pointer, sizeof(int))) {
+ PRINTM(MERROR, "Copy from user failed\n");
+ ret = -EFAULT;
+ goto done;
+ }
+ }
+
+ req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg));
+ if (req == NULL) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ cfg = (mlan_ds_misc_cfg *) req->pbuf;
+ cfg->sub_command = MLAN_OID_MISC_REGION;
+ req->req_id = MLAN_IOCTL_MISC_CFG;
+ if (!wrq->u.data.length)
+ req->action = MLAN_ACT_GET;
+ else {
+ req->action = MLAN_ACT_SET;
+ cfg->param.region_code = region;
+ }
+
+ if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
+ ret = -EFAULT;
+ goto done;
+ }
+
+ if (!wrq->u.data.length) {
+ wrq->u.data.length = 1;
+ if (copy_to_user
+ (wrq->u.data.pointer, &cfg->param.region_code, sizeof(int)))
+ ret = -EFAULT;
+ }
+ done:
+ if (req)
+ kfree(req);
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief Set/Get radio
+ *
+ * @param priv A pointer to moal_private structure
+ * @param wrq A pointer to iwreq structure
+ *
+ * @return 0 --success, otherwise fail
+ */
+static int
+woal_set_get_radio(moal_private * priv, struct iwreq *wrq)
+{
+ int ret = 0;
+ mlan_bss_info bss_info;
+ int option = 0;
+
+ ENTER();
+
+ memset(&bss_info, 0, sizeof(bss_info));
+
+ if (wrq->u.data.length) {
+ /* Set radio */
+ if (copy_from_user(&option, wrq->u.data.pointer, sizeof(int))) {
+ PRINTM(MERROR, "Copy from user failed\n");
+ ret = -EFAULT;
+ goto done;
+ }
+ if (MLAN_STATUS_SUCCESS != woal_set_radio(priv, (t_u8) option))
+ ret = -EFAULT;
+ } else {
+ /* Get radio status */
+ woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info);
+ wrq->u.data.length = 1;
+ if (copy_to_user
+ (wrq->u.data.pointer, &bss_info.radio_on,
+ sizeof(bss_info.radio_on))) {
+ PRINTM(MERROR, "Copy to user failed\n");
+ ret = -EFAULT;
+ }
+ }
+ done:
+ LEAVE();
+ return ret;
+}
+
+#ifdef DEBUG_LEVEL1
+/**
+ * @brief Get/Set the bit mask of driver debug message control
+ *
+ * @param priv A pointer to moal_private structure
+ * @param wrq A pointer to wrq structure
+ *
+ * @return 0 --success, otherwise fail
+ */
+static int
+woal_drv_dbg(moal_private * priv, struct iwreq *wrq)
+{
+ int data[4];
+ int ret = 0;
+
+ ENTER();
+
+ if (!wrq->u.data.length) {
+ data[0] = drvdbg;
+ /* 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 (wrq->u.data.length < 3) {
+ /* Get the driver debug bit masks from user */
+ if (copy_from_user
+ (data, wrq->u.data.pointer, sizeof(int) * wrq->u.data.length)) {
+ PRINTM(MERROR, "Copy from user failed\n");
+ ret = -EFAULT;
+ goto drvdbgexit;
+ }
+ drvdbg = data[0];
+ /* 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 "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 "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;
+
+ ENTER();
+ req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_wmm_cfg));
+ if (req == NULL) {
+ ret = -ENOMEM;
+ goto done;
+ }
+ cfg = (mlan_ds_wmm_cfg *) req->pbuf;
+ cfg->sub_command = MLAN_OID_WMM_CFG_QOS;
+ req->req_id = MLAN_IOCTL_WMM_CFG;
+ if (wrq->u.data.length) {
+ if (copy_from_user(&data, wrq->u.data.pointer, sizeof(int))) {
+ PRINTM(MERROR, "Copy from user failed\n");
+ ret = -EFAULT;
+ goto done;
+ }
+ req->action = MLAN_ACT_SET;
+ cfg->param.qos_cfg = (t_u8) data;
+ } else
+ req->action = MLAN_ACT_GET;
+ if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
+ ret = -EFAULT;
+ goto done;
+ }
+ if (!wrq->u.data.length) {
+ data = (int) cfg->param.qos_cfg;
+ if (copy_to_user(wrq->u.data.pointer, &data, sizeof(int))) {
+ ret = -EFAULT;
+ goto done;
+ }
+ wrq->u.data.length = 1;
+ }
+ done:
+ if (req)
+ kfree(req);
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief Set/Get WWS mode
+ *
+ * @param priv A pointer to moal_private structure
+ * @param wrq A pointer to iwreq structure
+ *
+ * @return 0 --success, otherwise fail
+ */
+static int
+woal_wws_cfg(moal_private * priv, struct iwreq *wrq)
+{
+ int ret = 0;
+ mlan_ds_misc_cfg *wws = NULL;
+ mlan_ioctl_req *req = NULL;
+ int data = 0;
+
+ ENTER();
+ req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg));
+ if (req == NULL) {
+ ret = -ENOMEM;
+ goto done;
+ }
+ wws = (mlan_ds_misc_cfg *) req->pbuf;
+ wws->sub_command = MLAN_OID_MISC_WWS;
+ req->req_id = MLAN_IOCTL_MISC_CFG;
+ if (wrq->u.data.length) {
+ if (copy_from_user(&data, wrq->u.data.pointer, sizeof(int))) {
+ PRINTM(MERROR, "Copy from user failed\n");
+ ret = -EFAULT;
+ goto done;
+ }
+ if (data != CMD_DISABLED && data != CMD_ENABLED) {
+ ret = -EINVAL;
+ goto done;
+ }
+ req->action = MLAN_ACT_SET;
+ wws->param.wws_cfg = data;
+ } else
+ req->action = MLAN_ACT_GET;
+ if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
+ ret = -EFAULT;
+ goto done;
+ }
+ if (!wrq->u.data.length) {
+ data = wws->param.wws_cfg;
+ if (copy_to_user(wrq->u.data.pointer, &data, sizeof(int))) {
+ ret = -EFAULT;
+ goto done;
+ }
+ wrq->u.data.length = 1;
+ }
+ done:
+ if (req)
+ kfree(req);
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief Set/Get sleep period
+ *
+ * @param priv A pointer to moal_private structure
+ * @param wrq A pointer to iwreq structure
+ *
+ * @return 0 --success, otherwise fail
+ */
+static int
+woal_sleep_pd(moal_private * priv, struct iwreq *wrq)
+{
+ int ret = 0;
+ mlan_ds_pm_cfg *pm_cfg = NULL;
+ mlan_ioctl_req *req = NULL;
+ int data = 0;
+
+ ENTER();
+
+ req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_pm_cfg));
+ if (req == NULL) {
+ ret = -ENOMEM;
+ goto done;
+ }
+ pm_cfg = (mlan_ds_pm_cfg *) req->pbuf;
+ pm_cfg->sub_command = MLAN_OID_PM_CFG_SLEEP_PD;
+ req->req_id = MLAN_IOCTL_PM_CFG;
+ if (wrq->u.data.length) {
+ if (copy_from_user(&data, wrq->u.data.pointer, sizeof(int))) {
+ PRINTM(MERROR, "Copy from user failed\n");
+ ret = -EFAULT;
+ goto done;
+ }
+ if ((data <= MAX_SLEEP_PERIOD && data >= MIN_SLEEP_PERIOD) ||
+ (data == 0)
+ || (data == SLEEP_PERIOD_RESERVED_FF)
+ ) {
+ req->action = MLAN_ACT_SET;
+ pm_cfg->param.sleep_period = data;
+ } else {
+ ret = -EINVAL;
+ goto done;
+ }
+ } else
+ req->action = MLAN_ACT_GET;
+
+ if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
+ ret = -EFAULT;
+ goto done;
+ }
+ if (!wrq->u.data.length) {
+ data = pm_cfg->param.sleep_period;
+ if (copy_to_user(wrq->u.data.pointer, &data, sizeof(int))) {
+ ret = -EFAULT;
+ goto done;
+ }
+ wrq->u.data.length = 1;
+ }
+
+ done:
+ if (req)
+ kfree(req);
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief 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;
+#ifdef DEBUG_LEVEL1
+ char err_str[][35] = { {"sleep clock error in ppm"},
+ {"wakeup offset in usec"},
+ {"clock stabilization time in usec"},
+ {"value of reserved for debug"}
+ };
+#endif
+
+ ENTER();
+
+ req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_pm_cfg));
+ if (req == NULL) {
+ LEAVE();
+ return -ENOMEM;
+ }
+
+ pm = (mlan_ds_pm_cfg *) req->pbuf;
+ pm->sub_command = MLAN_OID_PM_CFG_SLEEP_PARAMS;
+ req->req_id = MLAN_IOCTL_PM_CFG;
+ psleep_params = (pmlan_ds_sleep_params) & pm->param.sleep_params;
+
+ if (wrq->u.data.length == 0) {
+ req->action = MLAN_ACT_GET;
+ } else if (wrq->u.data.length == 6) {
+ if (copy_from_user(data, wrq->u.data.pointer, sizeof(int) *
+ wrq->u.data.length)) {
+ /* copy_from_user failed */
+ PRINTM(MERROR, "S_PARAMS: copy from user failed\n");
+ LEAVE();
+ return -EINVAL;
+ }
+#define MIN_VAL 0x0000
+#define MAX_VAL 0xFFFF
+ for (i = 0; i < 6; i++) {
+ if ((i == 3) || (i == 4)) {
+ /* These two cases are handled below the loop */
+ continue;
+ }
+ if (data[i] < MIN_VAL || data[i] > MAX_VAL) {
+ PRINTM(MERROR, "Invalid %s (0-65535)!\n", err_str[i]);
+ ret = -EINVAL;
+ goto done;
+ }
+ }
+ if (data[3] < 0 || data[3] > 2) {
+ PRINTM(MERROR, "Invalid control periodic calibration (0-2)!\n");
+ ret = -EINVAL;
+ goto done;
+ }
+ if (data[4] < 0 || data[4] > 2) {
+ PRINTM(MERROR, "Invalid control of external sleep clock (0-2)!\n");
+ ret = -EINVAL;
+ goto done;
+ }
+ req->action = MLAN_ACT_SET;
+ psleep_params->error = data[0];
+ psleep_params->offset = data[1];
+ psleep_params->stable_time = data[2];
+ psleep_params->cal_control = data[3];
+ psleep_params->ext_sleep_clk = data[4];
+ psleep_params->reserved = data[5];
+ } else {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
+ ret = -EFAULT;
+ goto done;
+ }
+
+ data[0] = psleep_params->error;
+ data[1] = psleep_params->offset;
+ data[2] = psleep_params->stable_time;
+ data[3] = psleep_params->cal_control;
+ data[4] = psleep_params->ext_sleep_clk;
+ data[5] = psleep_params->reserved;
+ wrq->u.data.length = 6;
+
+ if (copy_to_user(wrq->u.data.pointer, data, sizeof(int) *
+ wrq->u.data.length)) {
+ PRINTM(MERROR, "QCONFIG: copy to user failed\n");
+ ret = -EFAULT;
+ goto done;
+ }
+
+ done:
+ if (req)
+ kfree(req);
+
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @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;
+
+ 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;
+
+ if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
+ 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 (req)
+ 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;
+
+ 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;
+
+ if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
+ 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 (req)
+ 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;
+
+ 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;
+
+ if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
+ 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 (req)
+ 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;
+
+ 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;
+
+ if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
+ 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 (req)
+ 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;
+
+ ENTER();
+
+ req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_wmm_cfg));
+ if (req == NULL) {
+ ret = -ENOMEM;
+ goto done;
+ }
+ wmm = (mlan_ds_wmm_cfg *) req->pbuf;
+ req->req_id = MLAN_IOCTL_WMM_CFG;
+ wmm->sub_command = MLAN_OID_WMM_CFG_ENABLE;
+
+ if (wrq->u.data.length) {
+ /* Set WMM configuration */
+ if (copy_from_user(&data, wrq->u.data.pointer, sizeof(int))) {
+ PRINTM(MERROR, "Copy from user failed\n");
+ ret = -EFAULT;
+ goto done;
+ }
+ if ((data < CMD_DISABLED) || (data > CMD_ENABLED)) {
+ ret = -EINVAL;
+ goto done;
+ }
+ req->action = MLAN_ACT_SET;
+ if (data == CMD_DISABLED)
+ wmm->param.wmm_enable = MFALSE;
+ else
+ wmm->param.wmm_enable = MTRUE;
+ } else {
+ /* Get WMM status */
+ req->action = MLAN_ACT_GET;
+ }
+
+ if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
+ ret = -EFAULT;
+ goto done;
+ }
+
+ if (wrq->u.data.pointer) {
+ if (copy_to_user
+ (wrq->u.data.pointer, &wmm->param.wmm_enable,
+ sizeof(wmm->param.wmm_enable))) {
+ PRINTM(MERROR, "Copy to user failed\n");
+ ret = -EFAULT;
+ goto done;
+ }
+ wrq->u.data.length = 1;
+ }
+ done:
+ if (req)
+ kfree(req);
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief Implement 802.11D enable command
+ *
+ * @param priv Pointer to the moal_private driver data struct
+ * @param wrq Pointer to user data
+ *
+ * @return 0 --success, otherwise fail
+ */
+static int
+woal_11d_enable_ioctl(moal_private * priv, struct iwreq *wrq)
+{
+ int ret = 0;
+ mlan_ds_11d_cfg *pcfg_11d = NULL;
+ mlan_ioctl_req *req = NULL;
+ int data = 0;
+
+ ENTER();
+
+ req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11d_cfg));
+ if (req == NULL) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ pcfg_11d = (mlan_ds_11d_cfg *) req->pbuf;
+ req->req_id = MLAN_IOCTL_11D_CFG;
+ pcfg_11d->sub_command = MLAN_OID_11D_CFG_ENABLE;
+ if (wrq->u.data.length) {
+ /* Set 11D configuration */
+ if (copy_from_user(&data, wrq->u.data.pointer, sizeof(int))) {
+ PRINTM(MERROR, "Copy from user failed\n");
+ ret = -EFAULT;
+ goto done;
+ }
+ if ((data < CMD_DISABLED) || (data > CMD_ENABLED)) {
+ ret = -EINVAL;
+ goto done;
+ }
+ if (data == CMD_DISABLED)
+ pcfg_11d->param.enable_11d = MFALSE;
+ else
+ pcfg_11d->param.enable_11d = MTRUE;
+ req->action = MLAN_ACT_SET;
+ } else {
+ req->action = MLAN_ACT_GET;
+ }
+
+ if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
+ ret = -EFAULT;
+ goto done;
+ }
+
+ if (wrq->u.data.pointer) {
+ if (copy_to_user
+ (wrq->u.data.pointer, &pcfg_11d->param.enable_11d,
+ sizeof(pcfg_11d->param.enable_11d))) {
+ PRINTM(MERROR, "Copy to user failed\n");
+ ret = -EFAULT;
+ goto done;
+ }
+ wrq->u.data.length = 1;
+ }
+ done:
+ if (req)
+ kfree(req);
+
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief 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;
+
+ 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;
+
+ if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
+ ret = -EFAULT;
+ goto done;
+ }
+
+ done:
+ if (req)
+ kfree(req);
+
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief Control WPS Session Enable/Disable
+ *
+ * @param priv Pointer to the moal_private driver data struct
+ * @param wrq Pointer to user data
+ *
+ * @return 0 --success, otherwise fail
+ */
+static int
+woal_wps_cfg_ioctl(moal_private * priv, struct iwreq *wrq)
+{
+ int ret = 0;
+ mlan_ds_wps_cfg *pwps = NULL;
+ mlan_ioctl_req *req = NULL;
+ char buf[8];
+ struct iwreq *wreq = (struct iwreq *) wrq;
+
+ ENTER();
+
+ PRINTM(MINFO, "WOAL_WPS_SESSION\n");
+
+ memset(buf, 0, sizeof(buf));
+ if (copy_from_user(buf, wreq->u.data.pointer,
+ MIN(sizeof(buf) - 1, wreq->u.data.length))) {
+ PRINTM(MERROR, "Copy from user failed\n");
+ ret = -EFAULT;
+ goto done;
+ }
+
+ req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_wps_cfg));
+ if (req == NULL) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ pwps = (mlan_ds_wps_cfg *) req->pbuf;
+ req->req_id = MLAN_IOCTL_WPS_CFG;
+ req->action = MLAN_ACT_SET;
+ pwps->sub_command = MLAN_OID_WPS_CFG_SESSION;
+ if (buf[0] == 1)
+ pwps->param.wps_session = MLAN_WPS_CFG_SESSION_START;
+ else
+ pwps->param.wps_session = MLAN_WPS_CFG_SESSION_END;
+
+ if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
+ ret = -EFAULT;
+ goto done;
+ }
+
+ done:
+ if (req)
+ kfree(req);
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief Set WPA passphrase and SSID
+ *
+ * @param priv A pointer to moal_private structure
+ * @param wrq A pointer to user data
+ *
+ * @return 0 --success, otherwise fail
+ */
+static int
+woal_passphrase(moal_private * priv, struct iwreq *wrq)
+{
+ t_u16 len = 0;
+ static char buf[256];
+ char *begin, *end, *opt;
+ int ret = 0, action = -1, i;
+ mlan_ds_sec_cfg *sec = NULL;
+ mlan_ioctl_req *req = NULL;
+ t_u8 zero_mac[] = { 0, 0, 0, 0, 0, 0 };
+ t_u8 *mac = NULL;
+
+ ENTER();
+
+ if (!wrq->u.data.length || wrq->u.data.length >= sizeof(buf)) {
+ PRINTM(MERROR, "Argument missing or too long for setpassphrase\n");
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (copy_from_user(buf, wrq->u.data.pointer, wrq->u.data.length)) {
+ PRINTM(MERROR, "Copy from user failed\n");
+ ret = -EFAULT;
+ goto done;
+ }
+ buf[wrq->u.data.length] = '\0';
+
+ /* Parse the buf to get the cmd_action */
+ begin = buf;
+ end = woal_strsep(&begin, ';', '/');
+ if (end)
+ action = woal_atox(end);
+ if (action < 0 || action > 2 || end[1] != '\0') {
+ PRINTM(MERROR, "Invalid action argument %s\n", end);
+ ret = -EINVAL;
+ goto done;
+ }
+ req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg));
+ if (req == NULL) {
+ ret = -ENOMEM;
+ goto done;
+ }
+ sec = (mlan_ds_sec_cfg *) req->pbuf;
+ sec->sub_command = MLAN_OID_SEC_CFG_PASSPHRASE;
+ req->req_id = MLAN_IOCTL_SEC_CFG;
+ if (action == 0)
+ req->action = MLAN_ACT_GET;
+ else
+ req->action = MLAN_ACT_SET;
+ while (begin) {
+ end = woal_strsep(&begin, ';', '/');
+ opt = woal_strsep(&end, '=', '/');
+ if (!opt || !end || !end[0]) {
+ PRINTM(MERROR, "Invalid option\n");
+ ret = -EINVAL;
+ break;
+ } else if (!strnicmp(opt, "ssid", strlen(opt))) {
+ if (strlen(end) > MLAN_MAX_SSID_LENGTH) {
+ PRINTM(MERROR, "SSID length exceeds max length\n");
+ ret = -EFAULT;
+ break;
+ }
+ sec->param.passphrase.ssid.ssid_len = strlen(end);
+ strncpy((char *) sec->param.passphrase.ssid.ssid, end, strlen(end));
+ PRINTM(MINFO, "ssid=%s, len=%d\n", sec->param.passphrase.ssid.ssid,
+ (int) sec->param.passphrase.ssid.ssid_len);
+ } else if (!strnicmp(opt, "bssid", strlen(opt))) {
+ woal_mac2u8((t_u8 *) & sec->param.passphrase.bssid, end);
+ } else if (!strnicmp(opt, "psk", strlen(opt)) &&
+ req->action == MLAN_ACT_SET) {
+ if (strlen(end) != MLAN_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;
+ strncpy(sec->param.passphrase.psk.passphrase.passphrase, end,
+ 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;
+ if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
+ ret = -EFAULT;
+ goto done;
+ }
+ if (action == 0) {
+ memset(buf, 0, sizeof(buf));
+ if (sec->param.passphrase.ssid.ssid_len) {
+ len += sprintf(buf + len, "ssid:");
+ memcpy(buf + len, sec->param.passphrase.ssid.ssid,
+ sec->param.passphrase.ssid.ssid_len);
+ len += sec->param.passphrase.ssid.ssid_len;
+ len += sprintf(buf + len, " ");
+ }
+ if (memcmp(&sec->param.passphrase.bssid, zero_mac, sizeof(zero_mac))) {
+ mac = (t_u8 *) & sec->param.passphrase.bssid;
+ len += sprintf(buf + len, "bssid:");
+ for (i = 0; i < ETH_ALEN - 1; ++i)
+ len += sprintf(buf + len, "%02x:", mac[i]);
+ len += sprintf(buf + len, "%02x ", mac[i]);
+ }
+ if (sec->param.passphrase.psk_type == MLAN_PSK_PMK) {
+ len += sprintf(buf + len, "psk:");
+ for (i = 0; i < MLAN_MAX_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 (req)
+ kfree(req);
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief Get esupp mode
+ *
+ * @param priv A pointer to moal_private structure
+ * @param wrq A pointer to iwreq structure
+ *
+ * @return 0 --success, otherwise fail
+ */
+static int
+woal_get_esupp_mode(moal_private * priv, struct iwreq *wrq)
+{
+ int ret = 0;
+ mlan_ds_sec_cfg *sec = NULL;
+ mlan_ioctl_req *req = NULL;
+
+ ENTER();
+
+ req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg));
+ if (req == NULL) {
+ ret = -ENOMEM;
+ goto done;
+ }
+ sec = (mlan_ds_sec_cfg *) req->pbuf;
+ sec->sub_command = MLAN_OID_SEC_CFG_ESUPP_MODE;
+ req->req_id = MLAN_IOCTL_SEC_CFG;
+ req->action = MLAN_ACT_GET;
+
+ if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
+ ret = -EFAULT;
+ goto done;
+ }
+
+ if (copy_to_user
+ (wrq->u.data.pointer, (t_u8 *) & sec->param.esupp_mode,
+ sizeof(int) * 3)) {
+ ret = -EFAULT;
+ goto done;
+ }
+ wrq->u.data.length = 3;
+ done:
+ if (req)
+ kfree(req);
+ LEAVE();
+ return ret;
+}
+
+/** AES key length */
+#define AES_KEY_LEN 16
+/**
+ * @brief Adhoc AES control
+ *
+ * @param priv A pointer to moal_private structure
+ * @param wrq A pointer to user data
+ *
+ * @return 0 --success, otherwise fail
+ */
+static int
+woal_adhoc_aes_ioctl(moal_private * priv, struct iwreq *wrq)
+{
+ static char buf[256];
+ int ret = 0, action = -1;
+ unsigned int i;
+ t_u8 key_ascii[32];
+ t_u8 key_hex[16];
+ t_u8 *tmp;
+ mlan_bss_info bss_info;
+ mlan_ds_sec_cfg *sec = NULL;
+ mlan_ioctl_req *req = NULL;
+ t_u8 bcast_addr[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+ ENTER();
+
+ memset(key_ascii, 0x00, sizeof(key_ascii));
+ memset(key_hex, 0x00, sizeof(key_hex));
+
+ /* Get current BSS information */
+ memset(&bss_info, 0, sizeof(bss_info));
+ woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info);
+ if (bss_info.bss_mode != MLAN_BSS_MODE_IBSS ||
+ bss_info.media_connected == MTRUE) {
+ PRINTM(MERROR, "STA is connected or not in IBSS mode.\n");
+ ret = -EOPNOTSUPP;
+ goto done;
+ }
+
+ req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg));
+ if (req == NULL) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ if (wrq->u.data.length) {
+ if (wrq->u.data.length >= sizeof(buf)) {
+ PRINTM(MERROR, "Too many arguments\n");
+ ret = -EINVAL;
+ goto done;
+ }
+ if (copy_from_user(buf, wrq->u.data.pointer, wrq->u.data.length)) {
+ PRINTM(MERROR, "Copy from user failed\n");
+ ret = -EFAULT;
+ goto done;
+ }
+ buf[wrq->u.data.length] = '\0';
+
+ if (wrq->u.data.length == 1) {
+ /* Get Adhoc AES Key */
+ req->req_id = MLAN_IOCTL_SEC_CFG;
+ req->action = MLAN_ACT_GET;
+ sec = (mlan_ds_sec_cfg *) req->pbuf;
+ sec->sub_command = MLAN_OID_SEC_CFG_ENCRYPT_KEY;
+ sec->param.encrypt_key.key_len = AES_KEY_LEN;
+ sec->param.encrypt_key.key_index = MLAN_KEY_INDEX_UNICAST;
+ if (MLAN_STATUS_SUCCESS !=
+ woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
+ ret = -EFAULT;
+ goto done;
+ }
+
+ memcpy(key_hex, sec->param.encrypt_key.key_material,
+ sizeof(key_hex));
+ HEXDUMP("Adhoc AES Key (HEX)", key_hex, sizeof(key_hex));
+
+ wrq->u.data.length = sizeof(key_ascii) + 1;
+
+ tmp = key_ascii;
+ for (i = 0; i < sizeof(key_hex); i++)
+ tmp += sprintf((char *) tmp, "%02x", key_hex[i]);
+ } else if (wrq->u.data.length >= 2) {
+ /* Parse the buf to get the cmd_action */
+ action = woal_atox(&buf[0]);
+ if (action < 1 || action > 2) {
+ PRINTM(MERROR, "Invalid action argument %d\n", action);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ req->req_id = MLAN_IOCTL_SEC_CFG;
+ req->action = MLAN_ACT_SET;
+ sec = (mlan_ds_sec_cfg *) req->pbuf;
+ sec->sub_command = MLAN_OID_SEC_CFG_ENCRYPT_KEY;
+
+ if (action == 1) {
+ /* Set Adhoc AES Key */
+ memcpy(key_ascii, &buf[2], sizeof(key_ascii));
+ woal_ascii2hex(key_hex, (char *) key_ascii, sizeof(key_hex));
+ HEXDUMP("Adhoc AES Key (HEX)", key_hex, sizeof(key_hex));
+
+ sec->param.encrypt_key.key_len = AES_KEY_LEN;
+ sec->param.encrypt_key.key_index = MLAN_KEY_INDEX_UNICAST;
+ sec->param.encrypt_key.key_flags =
+ KEY_FLAG_SET_TX_KEY | KEY_FLAG_GROUP_KEY;
+ memcpy(sec->param.encrypt_key.mac_addr, (u8 *) bcast_addr,
+ ETH_ALEN);
+ memcpy(sec->param.encrypt_key.key_material, key_hex,
+ sec->param.encrypt_key.key_len);
+
+ if (MLAN_STATUS_SUCCESS !=
+ woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
+ ret = -EFAULT;
+ goto done;
+ }
+ } else if (action == 2) {
+ /* Clear Adhoc AES Key */
+ sec->param.encrypt_key.key_len = AES_KEY_LEN;
+ sec->param.encrypt_key.key_index = MLAN_KEY_INDEX_UNICAST;
+ sec->param.encrypt_key.key_flags = KEY_FLAG_REMOVE_KEY;
+ memcpy(sec->param.encrypt_key.mac_addr, (u8 *) bcast_addr,
+ ETH_ALEN);
+ memset(sec->param.encrypt_key.key_material, 0,
+ sizeof(sec->param.encrypt_key.key_material));
+
+ if (MLAN_STATUS_SUCCESS !=
+ woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
+ ret = -EFAULT;
+ goto done;
+ }
+ } else {
+ PRINTM(MERROR, "Invalid argument\n");
+ ret = -EINVAL;
+ goto done;
+ }
+ }
+
+ HEXDUMP("Adhoc AES Key (ASCII)", key_ascii, sizeof(key_ascii));
+ wrq->u.data.length = sizeof(key_ascii);
+ if (wrq->u.data.pointer) {
+ if (copy_to_user(wrq->u.data.pointer, &key_ascii,
+ sizeof(key_ascii))) {
+ PRINTM(MERROR, "copy_to_user failed\n");
+ ret = -EFAULT;
+ goto done;
+ }
+ }
+ }
+
+ done:
+ if (req)
+ kfree(req);
+
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief arpfilter ioctl function
+ *
+ * @param priv A pointer to moal_private structure
+ * @param wrq A pointer to iwreq structure
+ * @return 0 --success, otherwise fail
+ */
+static int
+woal_arp_filter(moal_private * priv, struct iwreq *wrq)
+{
+ int ret = 0;
+ mlan_ds_misc_cfg *misc = NULL;
+ mlan_ioctl_req *req = NULL;
+
+ ENTER();
+
+ req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg));
+ if (req == NULL) {
+ ret = -ENOMEM;
+ goto done;
+ }
+ misc = (mlan_ds_misc_cfg *) req->pbuf;
+ misc->sub_command = MLAN_OID_MISC_GEN_IE;
+ req->req_id = MLAN_IOCTL_MISC_CFG;
+ req->action = MLAN_ACT_SET;
+ misc->param.gen_ie.type = MLAN_IE_TYPE_ARP_FILTER;
+ misc->param.gen_ie.len = wrq->u.data.length;
+
+ /* get the whole command from user */
+ if (copy_from_user
+ (misc->param.gen_ie.ie_data, wrq->u.data.pointer, wrq->u.data.length)) {
+ PRINTM(MERROR, "copy from user failed\n");
+ ret = -EFAULT;
+ goto done;
+ }
+
+ if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
+ ret = -EFAULT;
+ goto done;
+ }
+ done:
+ if (req)
+ kfree(req);
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief 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];
+ struct iwreq *wreq = (struct iwreq *) wrq;
+ mlan_ioctl_req *ioctl_req = NULL;
+ mlan_ds_misc_cfg *misc = NULL;
+ int ret = 0, op_code = 0, data_length = wrq->u.data.length;
+
+ 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, wreq->u.data.pointer,
+ MIN(IPADDR_MAX_BUF - 1, wreq->u.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), (wreq->u.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[0]) != 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 */
+ if (MLAN_STATUS_SUCCESS !=
+ woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT)) {
+ 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 (ioctl_req)
+ 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;
+
+ 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 */
+ if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
+ 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 (req)
+ 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 MAX_TX_BF_GLOBAL_ARGS 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;
+ 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 > 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 TRIGGER_SOUNDING_FOR_PEER:
+ data_length += sprintf(buf + data_length,
+ "%02x:%02x:%02x:%02x:%02x:%02x",
+ bf_sound->peer_mac[0], bf_sound->peer_mac[1],
+ bf_sound->peer_mac[2], bf_sound->peer_mac[3],
+ bf_sound->peer_mac[4],
+ bf_sound->peer_mac[5]);
+ data_length += sprintf(buf + data_length, "%c", ';');
+ data_length += sprintf(buf + data_length, "%d", bf_sound->status);
+ 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 < 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 < 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;
+ default:
+ ret = -EINVAL;
+ goto done;
+ }
+ 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 Create a brief scan resp to relay basic BSS info to the app layer
+ *
+ * When the beacon/probe response has not been buffered, use the saved BSS
+ * information available to provide a minimum response for the application
+ * ioctl retrieval routines. Include:
+ * - Timestamp
+ * - Beacon Period
+ * - Capabilities (including WMM Element if available)
+ * - SSID
+ *
+ * @param ppbuffer Output parameter: Buffer used to create basic scan rsp
+ * @param pbss_desc Pointer to a BSS entry in the scan table to create
+ * scan response from for delivery to the application layer
+ *
+ * @return N/A
+ */
+static void
+wlan_scan_create_brief_table_entry(t_u8 ** ppbuffer,
+ BSSDescriptor_t * pbss_desc)
+{
+ t_u8 *ptmp_buf = *ppbuffer;
+ t_u8 tmp_ssid_hdr[2];
+ t_u8 ie_len = 0;
+
+ ENTER();
+
+ if (copy_to_user(ptmp_buf, pbss_desc->time_stamp,
+ sizeof(pbss_desc->time_stamp))) {
+ PRINTM(MINFO, "Copy to user failed\n");
+ LEAVE();
+ return;
+ }
+ ptmp_buf += sizeof(pbss_desc->time_stamp);
+
+ if (copy_to_user(ptmp_buf, &pbss_desc->beacon_period,
+ sizeof(pbss_desc->beacon_period))) {
+ PRINTM(MINFO, "Copy to user failed\n");
+ LEAVE();
+ return;
+ }
+ ptmp_buf += sizeof(pbss_desc->beacon_period);
+
+ if (copy_to_user
+ (ptmp_buf, &pbss_desc->cap_info, sizeof(pbss_desc->cap_info))) {
+ PRINTM(MINFO, "Copy to user failed\n");
+ LEAVE();
+ return;
+ }
+ ptmp_buf += sizeof(pbss_desc->cap_info);
+
+ tmp_ssid_hdr[0] = 0; /* Element ID for SSID is zero */
+ tmp_ssid_hdr[1] = pbss_desc->ssid.ssid_len;
+ if (copy_to_user(ptmp_buf, tmp_ssid_hdr, sizeof(tmp_ssid_hdr))) {
+ PRINTM(MINFO, "Copy to user failed\n");
+ LEAVE();
+ return;
+ }
+ ptmp_buf += sizeof(tmp_ssid_hdr);
+
+ if (copy_to_user(ptmp_buf, pbss_desc->ssid.ssid, pbss_desc->ssid.ssid_len)) {
+ PRINTM(MINFO, "Copy to user failed\n");
+ LEAVE();
+ return;
+ }
+ ptmp_buf += pbss_desc->ssid.ssid_len;
+
+ if (pbss_desc->wmm_ie.vend_hdr.element_id == WMM_IE) {
+ ie_len = sizeof(IEEEtypes_Header_t) + pbss_desc->wmm_ie.vend_hdr.len;
+ if (copy_to_user(ptmp_buf, &pbss_desc->wmm_ie, ie_len)) {
+ PRINTM(MINFO, "Copy to user failed\n");
+ LEAVE();
+ return;
+ }
+
+ ptmp_buf += ie_len;
+ }
+
+ if (pbss_desc->pwpa_ie) {
+ if ((*(pbss_desc->pwpa_ie)).vend_hdr.element_id == WPA_IE) {
+ ie_len =
+ sizeof(IEEEtypes_Header_t) +
+ (*(pbss_desc->pwpa_ie)).vend_hdr.len;
+ if (copy_to_user(ptmp_buf, pbss_desc->pwpa_ie, ie_len)) {
+ PRINTM(MINFO, "Copy to user failed\n");
+ LEAVE();
+ return;
+ }
+ }
+
+ ptmp_buf += ie_len;
+ }
+
+ if (pbss_desc->prsn_ie) {
+ if ((*(pbss_desc->prsn_ie)).ieee_hdr.element_id == RSN_IE) {
+ ie_len =
+ sizeof(IEEEtypes_Header_t) +
+ (*(pbss_desc->prsn_ie)).ieee_hdr.len;
+ if (copy_to_user(ptmp_buf, pbss_desc->prsn_ie, ie_len)) {
+ PRINTM(MINFO, "Copy to user failed\n");
+ LEAVE();
+ return;
+ }
+ }
+
+ ptmp_buf += ie_len;
+ }
+
+ *ppbuffer = ptmp_buf;
+ LEAVE();
+}
+
+/**
+ * @brief Create a wlan_ioctl_get_scan_table_entry for a given BSS
+ * Descriptor for inclusion in the ioctl response to the user space
+ * application.
+ *
+ *
+ * @param pbss_desc Pointer to a BSS entry in the scan table to form
+ * scan response from for delivery to the application layer
+ * @param ppbuffer Output parameter: Buffer used to output scan return struct
+ * @param pspace_left Output parameter: Number of bytes available in the
+ * response buffer.
+ *
+ * @return MLAN_STATUS_SUCCESS, or < 0 with IOCTL error code
+ */
+static int
+wlan_get_scan_table_ret_entry(BSSDescriptor_t * pbss_desc,
+ t_u8 ** ppbuffer, int *pspace_left)
+{
+ wlan_ioctl_get_scan_table_entry *prsp_entry;
+ wlan_ioctl_get_scan_table_entry tmp_rsp_entry;
+ int space_needed;
+ t_u8 *pcurrent;
+ int variable_size;
+
+ const int fixed_size = sizeof(wlan_ioctl_get_scan_table_entry);
+
+ ENTER();
+
+ pcurrent = *ppbuffer;
+
+ /* The variable size returned is the stored beacon size */
+ variable_size = pbss_desc->beacon_buf_size;
+
+ /* If we stored a beacon and its size was zero, set the variable size
+ return value to the size of the brief scan response
+ wlan_scan_create_brief_table_entry creates. Also used if we are not
+ configured to store beacons in the first place */
+ if (!variable_size) {
+ variable_size = pbss_desc->ssid.ssid_len + 2;
+ variable_size += (sizeof(pbss_desc->beacon_period)
+ + sizeof(pbss_desc->time_stamp)
+ + sizeof(pbss_desc->cap_info));
+ if (pbss_desc->wmm_ie.vend_hdr.element_id == WMM_IE) {
+ variable_size += (sizeof(IEEEtypes_Header_t)
+ + pbss_desc->wmm_ie.vend_hdr.len);
+ }
+
+ if (pbss_desc->pwpa_ie) {
+ if ((*(pbss_desc->pwpa_ie)).vend_hdr.element_id == WPA_IE) {
+ variable_size += (sizeof(IEEEtypes_Header_t)
+ + (*(pbss_desc->pwpa_ie)).vend_hdr.len);
+ }
+ }
+
+ if (pbss_desc->prsn_ie) {
+ if ((*(pbss_desc->prsn_ie)).ieee_hdr.element_id == RSN_IE) {
+ variable_size += (sizeof(IEEEtypes_Header_t)
+ + (*(pbss_desc->prsn_ie)).ieee_hdr.len);
+ }
+ }
+ }
+
+ space_needed = fixed_size + variable_size;
+
+ PRINTM(MINFO, "GetScanTable: need(%d), left(%d)\n",
+ space_needed, *pspace_left);
+
+ if (space_needed >= *pspace_left) {
+ *pspace_left = 0;
+ LEAVE();
+ return -E2BIG;
+ }
+
+ *pspace_left -= space_needed;
+
+ tmp_rsp_entry.fixed_field_length = (sizeof(tmp_rsp_entry)
+ -
+ sizeof(tmp_rsp_entry.fixed_field_length)
+ -
+ sizeof(tmp_rsp_entry.bss_info_length));
+
+ memcpy(tmp_rsp_entry.fixed_fields.bssid,
+ pbss_desc->mac_address, sizeof(prsp_entry->fixed_fields.bssid));
+
+ tmp_rsp_entry.fixed_fields.rssi = pbss_desc->rssi;
+ tmp_rsp_entry.fixed_fields.channel = pbss_desc->channel;
+ tmp_rsp_entry.fixed_fields.network_tsf = pbss_desc->network_tsf;
+ tmp_rsp_entry.bss_info_length = variable_size;
+
+ /*
+ * Copy fixed fields to user space
+ */
+ if (copy_to_user(pcurrent, &tmp_rsp_entry, fixed_size)) {
+ PRINTM(MINFO, "Copy to user failed\n");
+ LEAVE();
+ return -EFAULT;
+ }
+
+ pcurrent += fixed_size;
+
+ if (pbss_desc->pbeacon_buf) {
+ /*
+ * Copy variable length elements to user space
+ */
+ if (copy_to_user(pcurrent, pbss_desc->pbeacon_buf,
+ pbss_desc->beacon_buf_size)) {
+ PRINTM(MINFO, "Copy to user failed\n");
+ LEAVE();
+ return -EFAULT;
+ }
+
+ pcurrent += pbss_desc->beacon_buf_size;
+ } else {
+ wlan_scan_create_brief_table_entry(&pcurrent, pbss_desc);
+ }
+
+ *ppbuffer = pcurrent;
+
+ LEAVE();
+
+ return MLAN_STATUS_SUCCESS;
+}
+
+/**
+ * @brief Retrieve the scan response/beacon table
+ *
+ * @param wrq A pointer to iwreq structure
+ * @param scan_resp A pointer to mlan_scan_resp structure
+ * @param scan_start argument
+ *
+ * @return MLAN_STATUS_SUCCESS --success, otherwise fail
+ */
+static int
+moal_ret_get_scan_table_ioctl(struct iwreq *wrq,
+ mlan_scan_resp * scan_resp, t_u32 scan_start)
+{
+ pBSSDescriptor_t pbss_desc, scan_table;
+ 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 *) wrq->u.data.pointer;
+ pcurrent = (t_u8 *) prsp_info->scan_table_entry_buf;
+
+ pbuffer_end = wrq->u.data.pointer + wrq->u.data.length - 1;
+ space_left = pbuffer_end - pcurrent;
+ scan_table = (BSSDescriptor_t *) (scan_resp->pscan_table);
+
+ PRINTM(MINFO, "GetScanTable: scan_start req = %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 *) 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)
+{
+ int ret = 0;
+ mlan_ioctl_req *req = NULL;
+ mlan_ds_scan *scan = NULL;
+ t_u32 scan_start;
+ mlan_status status = MLAN_STATUS_SUCCESS;
+
+ ENTER();
+
+ /* Allocate an IOCTL request buffer */
+ req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_scan));
+ if (req == NULL) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ /* Fill request buffer */
+ scan = (mlan_ds_scan *) req->pbuf;
+ req->req_id = MLAN_IOCTL_SCAN;
+ req->action = MLAN_ACT_GET;
+
+ /* get the whole command from user */
+ if (copy_from_user(&scan_start, wrq->u.data.pointer, sizeof(scan_start))) {
+ PRINTM(MERROR, "copy from user failed\n");
+ ret = -EFAULT;
+ goto done;
+ }
+ if (scan_start) {
+ scan->sub_command = MLAN_OID_SCAN_NORMAL;
+ } else {
+ scan->sub_command = MLAN_OID_SCAN_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 (req && (status != MLAN_STATUS_PENDING))
+ kfree(req);
+ LEAVE();
+ return status;
+}
+
+/**
+ * @brief Set user scan
+ *
+ * @param priv A pointer to moal_private structure
+ * @param wrq A pointer to iwreq structure
+ *
+ * @return MLAN_STATUS_SUCCESS/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;
+
+ /* Allocate an IOCTL request buffer */
+ req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_scan) + wrq->u.data.length);
+ if (req == NULL) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ /* Fill request buffer */
+ scan = (mlan_ds_scan *) req->pbuf;
+ scan->sub_command = MLAN_OID_SCAN_USER_CONFIG;
+ req->req_id = MLAN_IOCTL_SCAN;
+ req->action = MLAN_ACT_SET;
+
+ if (copy_from_user(scan->param.user_scan.scan_cfg_buf,
+ wrq->u.data.pointer, wrq->u.data.length)) {
+ PRINTM(MINFO, "Copy from user failed\n");
+ LEAVE();
+ return -EFAULT;
+ }
+
+ /* Send IOCTL request to MLAN */
+ status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
+ if (status == MLAN_STATUS_SUCCESS) {
+ memset(&wrqu, 0, sizeof(union iwreq_data));
+ wireless_send_event(priv->netdev, SIOCGIWSCAN, &wrqu, NULL);
+ }
+ handle->scan_pending_on_block = MFALSE;
+ MOAL_REL_SEMAPHORE(&handle->async_sem);
+
+ done:
+ if (req && (status != MLAN_STATUS_PENDING))
+ kfree(req);
+ LEAVE();
+ return status;
+}
+
+/**
+ * @brief Cmd52 read/write register
+ *
+ * @param priv A pointer to moal_private structure
+ * @param wrq A pointer to iwreq structure
+ * @return MLAN_STATUS_SUCCESS --success, otherwise fail
+ */
+static int
+woal_cmd52rdwr_ioctl(moal_private * priv, struct iwreq *wrq)
+{
+ t_u8 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) {
+ 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 {
+ 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;
+ }
+ }
+
+ 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, func, 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();
+
+ if (!(buf = (t_u8 *) kmalloc(WOAL_2K_BYTES, GFP_KERNEL))) {
+ PRINTM(MERROR, "Cannot allocate buffer for command!\n");
+ ret = -EFAULT;
+ goto done;
+ }
+ if (!(data = (t_u8 *) kmalloc(WOAL_2K_BYTES, GFP_KERNEL))) {
+ PRINTM(MERROR, "Cannot allocate buffer for command!\n");
+ ret = -EFAULT;
+ goto done;
+ }
+ if (wrq->u.data.length > WOAL_2K_BYTES) {
+ PRINTM(MERROR, "Data lengh is too large!\n");
+ ret = -EINVAL;
+ goto done;
+ }
+ if (copy_from_user(buf, wrq->u.data.pointer, wrq->u.data.length)) {
+ PRINTM(MINFO, "Copy from user failed\n");
+ ret = -EFAULT;
+ goto done;
+ }
+
+ rw = buf[0]; /* read/write (0/1) */
+ func = buf[1]; /* func (0/1/2) */
+ reg = buf[5]; /* address */
+ reg = (reg << 8) + buf[4];
+ reg = (reg << 8) + buf[3];
+ reg = (reg << 8) + buf[2];
+ mode = buf[6]; /* byte mode/block mode (0/1) */
+ blklen = buf[8]; /* block size */
+ blklen = (blklen << 8) + buf[7];
+ blknum = buf[10]; /* block number or byte number */
+ blknum = (blknum << 8) + buf[9];
+
+ if (mode != BYTE_MODE)
+ mode = BLOCK_MODE;
+ total_len = (mode == BLOCK_MODE) ? blknum * blklen : blknum;
+ if (total_len > WOAL_2K_BYTES) {
+ PRINTM(MERROR, "Total data length is too large!\n");
+ ret = -EINVAL;
+ goto done;
+ }
+ PRINTM(MINFO, "CMD53 read/write, func = %d, addr = %#x, mode = %d, "
+ "block size = %d, block(byte) number = %d\n",
+ func, reg, mode, blklen, blknum);
+
+ if (!rw) {
+ sdio_claim_host(((struct sdio_mmc_card *) priv->phandle->card)->func);
+ if (sdio_readsb
+ (((struct sdio_mmc_card *) priv->phandle->card)->func, data, reg,
+ total_len))
+ PRINTM(MERROR, "sdio_readsb: reading memory 0x%x failed\n", reg);
+ sdio_release_host(((struct sdio_mmc_card *) priv->phandle->card)->func);
+
+ if (copy_to_user(wrq->u.data.pointer, data, 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 > total_len)
+ pattern_len = total_len;
+ memset(data, 0, sizeof(data));
+
+ /* Copy/duplicate the pattern to data buffer */
+ for (pos = 0; pos < total_len; pos++)
+ data[pos] = buf[11 + (pos % pattern_len)];
+
+ sdio_claim_host(((struct sdio_mmc_card *) priv->phandle->card)->func);
+ if (sdio_writesb
+ (((struct sdio_mmc_card *) priv->phandle->card)->func, reg, data,
+ total_len))
+ PRINTM(MERROR, "sdio_writesb: writing memory 0x%x failed\n", reg);
+ sdio_release_host(((struct sdio_mmc_card *) priv->phandle->card)->func);
+ }
+
+ done:
+ if (buf)
+ kfree(buf);
+ if (data)
+ kfree(data);
+ LEAVE();
+ return ret;
+}
+
+#if defined(SDIO_MULTI_PORT_TX_AGGR) || defined(SDIO_MULTI_PORT_RX_AGGR)
+/**
+ * @brief Set SDIO Multi-point aggregation control parameters
+ *
+ * @param priv A pointer to moal_private structure
+ * @param wrq A pointer to iwreq structure
+ *
+ * @return 0/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;
+ int ret = 0;
+ mlan_ds_misc_cfg *misc = NULL;
+ mlan_ioctl_req *req = NULL;
+
+ ENTER();
+
+ if (sizeof(int) * wrq->u.data.length > sizeof(data)) {
+ PRINTM(MERROR, "Too many arguments\n");
+ ret = -EINVAL;
+ goto done;
+ }
+
+ req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg));
+ if (req == NULL) {
+ ret = -ENOMEM;
+ goto done;
+ }
+ misc = (mlan_ds_misc_cfg *) req->pbuf;
+ memset(misc, 0, sizeof(mlan_ds_misc_cfg));
+
+ misc->sub_command = MLAN_OID_MISC_SDIO_MPA_CTRL;
+ req->req_id = MLAN_IOCTL_MISC_CFG;
+
+ /* Get the values first, then modify these values if user had modified them
+ */
+
+ req->action = MLAN_ACT_GET;
+ if ((ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) !=
+ MLAN_STATUS_SUCCESS) {
+ PRINTM(MERROR, "woal_request_ioctl returned %d\n", ret);
+ ret = -EFAULT;
+ goto done;
+ }
+
+ if (data_length == 0) {
+ data[0] = misc->param.mpa_ctrl.tx_enable;
+ data[1] = misc->param.mpa_ctrl.rx_enable;
+ data[2] = misc->param.mpa_ctrl.tx_buf_size;
+ data[3] = misc->param.mpa_ctrl.rx_buf_size;
+ data[4] = misc->param.mpa_ctrl.tx_max_ports;
+ data[5] = misc->param.mpa_ctrl.rx_max_ports;
+
+ PRINTM(MINFO, "Get Param: %d %d %d %d %d %d\n", data[0], data[1],
+ data[2], data[3], data[4], data[5]);
+
+ if (copy_to_user(wrq->u.data.pointer, data, sizeof(data))) {
+ ret = -EFAULT;
+ goto done;
+ }
+ wrq->u.data.length = sizeof(data) / sizeof(int);
+ goto done;
+ }
+
+ if (copy_from_user(data, wrq->u.data.pointer, sizeof(int) * data_length)) {
+ 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];
+ case 5:
+ misc->param.mpa_ctrl.tx_max_ports = data[4];
+ case 4:
+ misc->param.mpa_ctrl.rx_buf_size = data[3];
+ case 3:
+ misc->param.mpa_ctrl.tx_buf_size = data[2];
+ case 2:
+ misc->param.mpa_ctrl.rx_enable = data[1];
+ case 1:
+ /* Set cmd */
+ req->action = MLAN_ACT_SET;
+
+ PRINTM(MINFO, "Set Param: %d %d %d %d %d %d\n", data[0], data[1],
+ data[2], data[3], data[4], data[5]);
+
+ misc->param.mpa_ctrl.tx_enable = data[0];
+ break;
+ default:
+ PRINTM(MERROR, "Default case error\n");
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if ((ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT) !=
+ MLAN_STATUS_SUCCESS)) {
+ PRINTM(MERROR, "woal_request_ioctl returned %d\n", ret);
+ ret = -EFAULT;
+ goto done;
+ }
+
+ done:
+ if (req)
+ kfree(req);
+ LEAVE();
+ return ret;
+}
+#endif /* SDIO_MULTI_PORT_TX_AGGR || SDIO_MULTI_PORT_RX_AGGR */
+
+/**
+ * @brief Set/Get 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 arg_len = 7;
+ int data[arg_len];
+ mlan_ds_scan *scan = NULL;
+ mlan_ioctl_req *req = NULL;
+
+ ENTER();
+ req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_scan));
+ if (req == NULL) {
+ ret = -ENOMEM;
+ goto done;
+ }
+ if (wrq->u.data.length > arg_len) {
+ ret = -EINVAL;
+ goto done;
+ }
+ scan = (mlan_ds_scan *) req->pbuf;
+ scan->sub_command = MLAN_OID_SCAN_CONFIG;
+ req->req_id = MLAN_IOCTL_SCAN;
+ memset(data, 0, sizeof(data));
+ if (wrq->u.data.length) {
+ if (copy_from_user
+ (data, wrq->u.data.pointer, (wrq->u.data.length * sizeof(int)))) {
+ PRINTM(MERROR, "Copy from user failed\n");
+ ret = -EFAULT;
+ goto done;
+ }
+ if ((data[0] < 0) || (data[0] > MLAN_SCAN_TYPE_PASSIVE)) {
+ PRINTM(MERROR, "Invalid argument for scan type\n");
+ ret = -EINVAL;
+ goto done;
+ }
+ if ((data[1] < 0) || (data[1] > MLAN_SCAN_MODE_ANY)) {
+ PRINTM(MERROR, "Invalid argument for scan mode\n");
+ ret = -EINVAL;
+ goto done;
+ }
+ if ((data[2] < 0) || (data[2] > MAX_PROBES)) {
+ PRINTM(MERROR, "Invalid argument for scan probes\n");
+ ret = -EINVAL;
+ goto done;
+ }
+ if (((data[3] < 0) || (data[3] > MRVDRV_MAX_ACTIVE_SCAN_CHAN_TIME)) ||
+ ((data[4] < 0) || (data[4] > MRVDRV_MAX_ACTIVE_SCAN_CHAN_TIME)) ||
+ ((data[5] < 0) || (data[5] > MRVDRV_MAX_PASSIVE_SCAN_CHAN_TIME))) {
+ PRINTM(MERROR, "Invalid argument for scan time\n");
+ ret = -EINVAL;
+ goto done;
+ }
+ if ((data[6] < 0) || (data[6] > 1)) {
+ PRINTM(MERROR, "Invalid argument for extended scan\n");
+ ret = -EINVAL;
+ goto done;
+ }
+ req->action = MLAN_ACT_SET;
+ memcpy(&scan->param.scan_cfg, data, sizeof(data));
+ } else
+ req->action = MLAN_ACT_GET;
+ if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
+ ret = -EFAULT;
+ goto done;
+ }
+ if (!wrq->u.data.length) {
+ memcpy(data, &scan->param.scan_cfg, sizeof(data));
+ if (copy_to_user(wrq->u.data.pointer, data, sizeof(data))) {
+ ret = -EFAULT;
+ goto done;
+ }
+ wrq->u.data.length = sizeof(data) / sizeof(int);
+ }
+ done:
+ if (req)
+ kfree(req);
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief Set/Get PS configuration parameters
+ *
+ * @param priv A pointer to moal_private structure
+ * @param wrq A pointer to iwreq structure
+ *
+ * @return 0 --success, otherwise fail
+ */
+static int
+woal_set_get_ps_cfg(moal_private * priv, struct iwreq *wrq)
+{
+ int data[7], ret = 0;
+ mlan_ds_pm_cfg *pm_cfg = NULL;
+ mlan_ioctl_req *req = NULL;
+ int allowed = 3;
+ int i = 3;
+
+ ENTER();
+
+ allowed++; /* For ad-hoc awake period parameter */
+ allowed++; /* For beacon missing timeout parameter */
+ allowed += 2; /* For delay to PS and PS mode parameters */
+
+ req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_pm_cfg));
+ if (req == NULL) {
+ ret = -ENOMEM;
+ goto done;
+ }
+ if (wrq->u.data.length > allowed) {
+ ret = -EINVAL;
+ goto done;
+ }
+ pm_cfg = (mlan_ds_pm_cfg *) req->pbuf;
+ pm_cfg->sub_command = MLAN_OID_PM_CFG_PS_CFG;
+ req->req_id = MLAN_IOCTL_PM_CFG;
+ memset(data, 0, sizeof(data));
+ if (wrq->u.data.length) {
+ if (copy_from_user
+ (data, wrq->u.data.pointer, (wrq->u.data.length * sizeof(int)))) {
+ PRINTM(MERROR, "Copy from user failed\n");
+ ret = -EFAULT;
+ goto done;
+ }
+ if ((data[0] < PS_NULL_DISABLE)) {
+ PRINTM(MERROR, "Invalid argument for PS null interval\n");
+ ret = -EINVAL;
+ goto done;
+ }
+ if ((data[1] != 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] != SPECIAL_ADHOC_AWAKE_PD) &&
+ ((data[i] < MIN_ADHOC_AWAKE_PD) ||
+ (data[i] > MAX_ADHOC_AWAKE_PD))) {
+ PRINTM(MERROR, "Invalid argument for adhoc awake period\n");
+ ret = -EINVAL;
+ goto done;
+ }
+ i++;
+ if ((data[i] != DISABLE_BCN_MISS_TO) &&
+ ((data[i] < MIN_BCN_MISS_TO) || (data[i] > MAX_BCN_MISS_TO))) {
+ PRINTM(MERROR, "Invalid argument for beacon miss timeout\n");
+ ret = -EINVAL;
+ goto done;
+ }
+ i++;
+ if (wrq->u.data.length < allowed - 1)
+ data[i] = DELAY_TO_PS_UNCHANGED;
+ else if ((data[i] < MIN_DELAY_TO_PS) || (data[i] > MAX_DELAY_TO_PS)) {
+ PRINTM(MERROR, "Invalid argument for delay to PS\n");
+ ret = -EINVAL;
+ goto done;
+ }
+ i++;
+ if ((data[i] != PS_MODE_UNCHANGED) && (data[i] != PS_MODE_AUTO) &&
+ (data[i] != PS_MODE_POLL) && (data[i] != PS_MODE_NULL)) {
+ PRINTM(MERROR, "Invalid argument for PS mode\n");
+ ret = -EINVAL;
+ goto done;
+ }
+ i++;
+ req->action = MLAN_ACT_SET;
+ memcpy(&pm_cfg->param.ps_cfg, data,
+ MIN(sizeof(pm_cfg->param.ps_cfg), sizeof(data)));
+ } else
+ req->action = MLAN_ACT_GET;
+
+ if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
+ ret = -EFAULT;
+ goto done;
+ }
+ memcpy(data, &pm_cfg->param.ps_cfg,
+ MIN((sizeof(int) * allowed), sizeof(pm_cfg->param.ps_cfg)));
+ if (copy_to_user(wrq->u.data.pointer, data, sizeof(int) * allowed)) {
+ ret = -EFAULT;
+ goto done;
+ }
+ wrq->u.data.length = allowed;
+
+ 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;
+ woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
+ }
+
+ done:
+ if (req)
+ kfree(req);
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief Private IOCTL entry to send an ADDTS TSPEC
+ *
+ * Receive a ADDTS command from the application. The command structure
+ * contains a TSPEC and timeout in milliseconds. The timeout is performed
+ * in the firmware after the ADDTS command frame is sent.
+ *
+ * The TSPEC is received in the API as an opaque block. 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;
+
+ 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 = (t_u8) addts_ioctl.ie_data_len;
+
+ memcpy(cfg->param.addts.ie_data,
+ addts_ioctl.ie_data, cfg->param.addts.ie_data_len);
+
+ if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req,
+ MOAL_IOCTL_WAIT)) {
+ ret = -EFAULT;
+ goto done;
+ }
+ addts_ioctl.cmd_result = cfg->param.addts.result;
+ addts_ioctl.ieee_status_code = (t_u8) cfg->param.addts.status_code;
+ addts_ioctl.ie_data_len = cfg->param.addts.ie_data_len;
+
+ memcpy(addts_ioctl.ie_data,
+ cfg->param.addts.ie_data, cfg->param.addts.ie_data_len);
+
+ 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 (req)
+ kfree(req);
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief Private IOCTL entry to send a DELTS TSPEC
+ *
+ * Receive a DELTS command from the application. The command structure
+ * contains a TSPEC and reason code along with space for a command result
+ * to be returned. The information is packaged is sent to the wlan_cmd.c
+ * firmware command prep and send routines for execution in the firmware.
+ *
+ * The reason code is not used for WMM implementations but is indicated in
+ * the 802.11e specification.
+ *
+ * The return value to the application layer indicates a driver execution
+ * success or failure. A successful return could still indicate a firmware
+ * failure via the cmd_result field copied back to the application.
+ *
+ * @param priv Pointer to the mlan_private driver data struct
+ * @param wrq A pointer to iwreq structure containing the
+ * wlan_ioctl_wmm_delts_req_t struct for this DELTS request
+ *
+ * @return 0 if successful; IOCTL error code otherwise
+ */
+static int
+woal_wmm_delts_req_ioctl(moal_private * priv, struct iwreq *wrq)
+{
+ mlan_ioctl_req *req = NULL;
+ mlan_ds_wmm_cfg *cfg = NULL;
+ wlan_ioctl_wmm_delts_req_t delts_ioctl;
+ int ret = 0;
+
+ ENTER();
+
+ req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_wmm_cfg));
+ if (req == NULL) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ req->req_id = MLAN_IOCTL_WMM_CFG;
+ cfg = (mlan_ds_wmm_cfg *) req->pbuf;
+ cfg->sub_command = MLAN_OID_WMM_CFG_DELTS;
+
+ memset(&delts_ioctl, 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;
+
+ memcpy(cfg->param.delts.ie_data,
+ delts_ioctl.ie_data, cfg->param.delts.ie_data_len);
+
+ if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req,
+ MOAL_IOCTL_WAIT)) {
+ ret = -EFAULT;
+ goto done;
+ }
+
+ /* Return the firmware command result back to the application layer */
+ delts_ioctl.cmd_result = cfg->param.delts.result;
+ wrq->u.data.length = sizeof(delts_ioctl);
+
+ if (copy_to_user(wrq->u.data.pointer, &delts_ioctl, wrq->u.data.length)) {
+ PRINTM(MERROR, "TSPEC: DELTS copy to user failed\n");
+ ret = -EFAULT;
+ goto done;
+ }
+ }
+
+ done:
+ if (req)
+ kfree(req);
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief Private IOCTL entry to get/set a specified AC Queue's parameters
+ *
+ * Receive a AC Queue configuration command which is used to get, set, or
+ * default the parameters associated with a specific WMM AC Queue.
+ *
+ * @param priv Pointer to the mlan_private driver data struct
+ * @param wrq A pointer to iwreq structure containing the
+ * wlan_ioctl_wmm_queue_config_t struct
+ *
+ * @return 0 if successful; IOCTL error code otherwise
+ */
+static int
+woal_wmm_queue_config_ioctl(moal_private * priv, struct iwreq *wrq)
+{
+ mlan_ioctl_req *req = NULL;
+ mlan_ds_wmm_cfg *pwmm = NULL;
+ mlan_ds_wmm_queue_config *pqcfg = NULL;
+ wlan_ioctl_wmm_queue_config_t qcfg_ioctl;
+ int ret = 0;
+
+ ENTER();
+
+ req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_wmm_cfg));
+ if (req == NULL) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ req->req_id = MLAN_IOCTL_WMM_CFG;
+ pwmm = (mlan_ds_wmm_cfg *) req->pbuf;
+ pwmm->sub_command = MLAN_OID_WMM_CFG_QUEUE_CONFIG;
+
+ memset(&qcfg_ioctl, 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;
+
+ if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req,
+ MOAL_IOCTL_WAIT)) {
+ 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 (req)
+ kfree(req);
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief Private IOCTL entry to get and start/stop queue stats on a WMM AC
+ *
+ * Receive a AC Queue statistics command from the application for a specific
+ * WMM AC. The command can:
+ * - Turn stats on
+ * - Turn stats off
+ * - Collect and clear the stats
+ *
+ * @param priv Pointer to the moal_private driver data struct
+ * @param wrq A pointer to iwreq structure containing the
+ * wlan_ioctl_wmm_queue_stats_t struct
+ *
+ * @return 0 if successful; IOCTL error code otherwise
+ */
+static int
+woal_wmm_queue_stats_ioctl(moal_private * priv, struct iwreq *wrq)
+{
+ mlan_ioctl_req *req = NULL;
+ mlan_ds_wmm_cfg *pwmm = NULL;
+ mlan_ds_wmm_queue_stats *pqstats = NULL;
+ wlan_ioctl_wmm_queue_stats_t qstats_ioctl;
+ int ret = 0;
+
+ ENTER();
+
+ req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_wmm_cfg));
+ if (req == NULL) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ req->req_id = MLAN_IOCTL_WMM_CFG;
+ pwmm = (mlan_ds_wmm_cfg *) req->pbuf;
+ pwmm->sub_command = MLAN_OID_WMM_CFG_QUEUE_STATS;
+
+ memset(&qstats_ioctl, 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;
+ }
+
+ memcpy((void *) pqstats, (void *) &qstats_ioctl, sizeof(qstats_ioctl));
+ PRINTM(MINFO, "QSTATS: IOCTL [%d,%d]\n", qstats_ioctl.action,
+ qstats_ioctl.user_priority);
+
+ if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req,
+ MOAL_IOCTL_WAIT)) {
+ ret = -EFAULT;
+ goto done;
+ }
+
+ memset(&qstats_ioctl, 0x00, sizeof(qstats_ioctl));
+ memcpy((void *) &qstats_ioctl, (void *) pqstats, sizeof(qstats_ioctl));
+ wrq->u.data.length = sizeof(qstats_ioctl);
+
+ if (copy_to_user
+ (wrq->u.data.pointer, &qstats_ioctl, wrq->u.data.length)) {
+ PRINTM(MERROR, "QSTATS: copy to user failed\n");
+ ret = -EFAULT;
+ goto done;
+ }
+ }
+
+ done:
+ if (req)
+ kfree(req);
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief Private IOCTL entry to get the status of the WMM queues
+ *
+ * Return the following information for each WMM AC:
+ * - WMM IE Acm Required
+ * - Firmware Flow Required
+ * - Firmware Flow Established
+ * - Firmware Queue Enabled
+ * - Firmware Delivery Enabled
+ * - Firmware Trigger Enabled
+ *
+ * @param priv Pointer to the moal_private driver data struct
+ * @param wrq A pointer to iwreq structure containing the
+ * wlan_ioctl_wmm_queue_status_t struct for request
+ *
+ * @return 0 if successful; IOCTL error code otherwise
+ */
+static int
+woal_wmm_queue_status_ioctl(moal_private * priv, struct iwreq *wrq)
+{
+ mlan_ioctl_req *req = NULL;
+ mlan_ds_wmm_cfg *pwmm = NULL;
+ wlan_ioctl_wmm_queue_status_t qstatus_ioctl;
+ int ret = 0;
+
+ ENTER();
+
+ req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_wmm_cfg));
+ if (req == NULL) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ req->req_id = MLAN_IOCTL_WMM_CFG;
+ pwmm = (mlan_ds_wmm_cfg *) req->pbuf;
+ pwmm->sub_command = MLAN_OID_WMM_CFG_QUEUE_STATUS;
+
+ if (wrq->u.data.length == sizeof(qstatus_ioctl)) {
+ if (MLAN_STATUS_SUCCESS !=
+ woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
+ ret = -EFAULT;
+ goto done;
+ }
+
+ memset(&qstatus_ioctl, 0x00, sizeof(qstatus_ioctl));
+ memcpy((void *) &qstatus_ioctl, (void *) &pwmm->param.q_status,
+ sizeof(qstatus_ioctl));
+ wrq->u.data.length = sizeof(qstatus_ioctl);
+ } else {
+ wrq->u.data.length = 0;
+ }
+
+ if (copy_to_user(wrq->u.data.pointer, &qstatus_ioctl, wrq->u.data.length)) {
+ PRINTM(MERROR, "QSTATUS: copy to user failed\n");
+ ret = -EFAULT;
+ goto done;
+ }
+
+ done:
+ if (req)
+ kfree(req);
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief Private IOCTL entry to get the status of the WMM Traffic Streams
+ *
+ * @param priv Pointer to the moal_private driver data struct
+ * @param wrq A pointer to iwreq structure containing the
+ * wlan_ioctl_wmm_ts_status_t struct for request
+ *
+ * @return 0 if successful; IOCTL error code otherwise
+ */
+static int
+woal_wmm_ts_status_ioctl(moal_private * priv, struct iwreq *wrq)
+{
+ mlan_ioctl_req *req = NULL;
+ mlan_ds_wmm_cfg *pwmm = NULL;
+ wlan_ioctl_wmm_ts_status_t ts_status_ioctl;
+ int ret = 0;
+
+ ENTER();
+
+ req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_wmm_cfg));
+ if (req == NULL) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ req->req_id = MLAN_IOCTL_WMM_CFG;
+ pwmm = (mlan_ds_wmm_cfg *) req->pbuf;
+ pwmm->sub_command = MLAN_OID_WMM_CFG_TS_STATUS;
+
+ memset(&ts_status_ioctl, 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;
+
+ if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req,
+ MOAL_IOCTL_WAIT)) {
+ ret = -EFAULT;
+ goto done;
+ }
+
+ memset(&ts_status_ioctl, 0x00, sizeof(ts_status_ioctl));
+ memcpy((void *) &ts_status_ioctl, (void *) &pwmm->param.ts_status,
+ sizeof(ts_status_ioctl));
+ wrq->u.data.length = sizeof(ts_status_ioctl);
+ } else {
+ wrq->u.data.length = 0;
+ }
+
+ if (copy_to_user(wrq->u.data.pointer, &ts_status_ioctl, wrq->u.data.length)) {
+ PRINTM(MERROR, "TS_STATUS: copy to user failed\n");
+ ret = -EFAULT;
+ goto done;
+ }
+
+ done:
+ if (req)
+ 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;
+ 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 */
+ if (woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT) != 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 (req)
+ kfree(req);
+
+ LEAVE();
+ return ret;
+}
+
+#if defined(DFS_TESTING_SUPPORT)
+/**
+ * @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];
+ int data_length = wrq->u.data.length;
+ ENTER();
+
+ /* 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, sizeof(int) * data_length)) {
+ PRINTM(MERROR, "Copy from user failed\n");
+ ret = -EFAULT;
+ goto done;
+ }
+ if ((unsigned) data[0] > 0xFFFF) {
+ PRINTM(MERROR, "The maximum user CAC is 65535 msec.\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 / 1000;
+ req->action = MLAN_ACT_SET;
+ } else {
+ PRINTM(MERROR, "Invalid number of args!\n");
+ ret = -EINVAL;
+ goto done;
+ }
+
+ /* Send IOCTL request to MLAN */
+ if (woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT) != 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 (req)
+ kfree(req);
+
+ LEAVE();
+ return ret;
+}
+#endif /* DFS_SUPPORT && DFS_TESTING_SUPPORT */
+
+/**
+ * @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;
+
+ 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 */
+ if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
+ 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 (req)
+ 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[2] = { 0 };
+
+ ENTER();
+
+ if (wrq->u.data.length > 2) {
+ PRINTM(MERROR, "Invalid number of argument!\n");
+ ret = -EFAULT;
+ goto done;
+ }
+ req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_radio_cfg));
+ if (req == NULL) {
+ ret = -ENOMEM;
+ goto done;
+ }
+ radio = (mlan_ds_radio_cfg *) req->pbuf;
+ radio->sub_command = MLAN_OID_ANT_CFG;
+ req->req_id = MLAN_IOCTL_RADIO_CFG;
+ if (wrq->u.data.length) {
+ if (copy_from_user(data, wrq->u.data.pointer, sizeof(data))) {
+ PRINTM(MERROR, "Copy from user failed\n");
+ ret = -EFAULT;
+ goto done;
+ }
+ 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];
+ req->action = MLAN_ACT_SET;
+ } else
+ req->action = MLAN_ACT_GET;
+ if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
+ ret = -EFAULT;
+ goto done;
+ }
+ if (!wrq->u.data.length) {
+ wrq->u.data.length = 1;
+ 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;
+ if (copy_to_user(wrq->u.data.pointer, data, sizeof(data))) {
+ ret = -EFAULT;
+ goto done;
+ }
+ }
+ done:
+ if (req)
+ 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(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;
+ 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;
+ case WOAL_SET_GET_WWS_CFG:
+ ret = woal_wws_cfg(priv, wrq);
+ break;
+ case WOAL_SLEEP_PD:
+ ret = woal_sleep_pd(priv, wrq);
+ break;
+ case WOAL_AUTH_TYPE:
+ ret = woal_auth_type(priv, wrq);
+ break;
+ case WOAL_PORT_CTRL:
+ ret = woal_port_ctrl(priv, wrq);
+ break;
+#if defined(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;
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
+ break;
+
+ case WOAL_SET_GET_SIXTEEN_INT:
+ switch ((int) wrq->u.data.flags) {
+ case WOAL_TX_POWERCFG:
+ ret = woal_tx_power_cfg(priv, wrq);
+ break;
+#ifdef DEBUG_LEVEL1
+ case WOAL_DRV_DBG:
+ ret = woal_drv_dbg(priv, wrq);
+ break;
+#endif
+ case WOAL_BEACON_INTERVAL:
+ ret = woal_beacon_interval(priv, wrq);
+ break;
+ case WOAL_ATIM_WINDOW:
+ ret = woal_atim_window(priv, wrq);
+ break;
+ case WOAL_SIGNAL:
+ ret = woal_get_signal(priv, wrq);
+ break;
+ case WOAL_DEEP_SLEEP:
+ ret = woal_deep_sleep_ioctl(priv, wrq);
+ break;
+ case WOAL_11N_TX_CFG:
+ ret = woal_11n_tx_cfg(priv, wrq);
+ break;
+ case WOAL_11N_AMSDU_AGGR_CTRL:
+ ret = woal_11n_amsdu_aggr_ctrl(priv, wrq);
+ break;
+ case WOAL_11N_HTCAP_CFG:
+ ret = woal_11n_htcap_cfg(priv, wrq);
+ break;
+ case WOAL_PRIO_TBL:
+ ret = woal_11n_prio_tbl(priv, wrq);
+ break;
+ case WOAL_ADDBA_UPDT:
+ ret = woal_addba_para_updt(priv, wrq);
+ break;
+ case WOAL_ADDBA_REJECT:
+ ret = woal_addba_reject(priv, wrq);
+ break;
+ case WOAL_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;
+ 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_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;
+#if defined(SDIO_MULTI_PORT_TX_AGGR) || defined(SDIO_MULTI_PORT_RX_AGGR)
+ case WOAL_SDIO_MPA_CTRL:
+ ret = woal_do_sdio_mpa_ctrl(priv, wrq);
+ break;
+#endif
+ case WOAL_SLEEP_PARAMS:
+ ret = woal_sleep_params_ioctl(priv, wrq);
+ break;
+#if defined(DFS_TESTING_SUPPORT)
+ case WOAL_DFS_TESTING:
+ ret = woal_dfs_testing(priv, wrq);
+ break;
+#endif
+ case WOAL_MGMT_FRAME_CTRL:
+ ret = woal_mgmt_frame_passthru_ctrl(priv, wrq);
+ break;
+ case WOAL_SET_GET_TX_RX_ANT:
+ ret = woal_set_get_tx_rx_ant(priv, wrq);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ break;
+
+ case WOALGETLOG:
+ ret = woal_get_log(priv, wrq);
+ break;
+
+ case WOAL_SET_GET_256_CHAR:
+ switch (wrq->u.data.flags) {
+ case WOAL_PASSPHRASE:
+ ret = woal_passphrase(priv, wrq);
+ break;
+ case WOAL_ADHOC_AES:
+ ret = woal_adhoc_aes_ioctl(priv, wrq);
+ break;
+ case WOAL_WMM_QUEUE_STATUS:
+ ret = woal_wmm_queue_status_ioctl(priv, wrq);
+ break;
+
+ case WOAL_WMM_TS_STATUS:
+ ret = woal_wmm_ts_status_ioctl(priv, wrq);
+ break;
+ 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) {
+ case WOAL_CMD_53RDWR:
+ ret = woal_cmd53rdwr_ioctl(priv, wrq);
+ break;
+ case WOAL_SET_USER_SCAN:
+ ret = woal_set_user_scan_ioctl(priv, wrq);
+ break;
+ case WOAL_GET_SCAN_TABLE:
+ ret = woal_get_scan_table_ioctl(priv, wrq);
+ break;
+ case WOAL_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(cfg80211_wext) && MLAN_STATUS_SUCCESS !=
+ woal_disconnect(priv, MOAL_IOCTL_WAIT, NULL)) {
+ ret = -EFAULT;
+ }
+ break;
+#endif
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief Get mode
+ *
+ * @param priv A pointer to moal_private structure
+ * @param wait_option Wait option (MOAL_WAIT or MOAL_NO_WAIT)
+ *
+ * @return Wireless mode
+ */
+t_u32
+woal_get_mode(moal_private * priv, t_u8 wait_option)
+{
+ int ret = 0;
+ mlan_ds_bss *bss = NULL;
+ mlan_ioctl_req *req = NULL;
+ mlan_status status = MLAN_STATUS_SUCCESS;
+ t_u32 mode = priv->w_stats.status;
+ ENTER();
+
+ /* Allocate an IOCTL request buffer */
+ req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss));
+ if (req == NULL) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ /* Fill request buffer */
+ bss = (mlan_ds_bss *) req->pbuf;
+ bss->sub_command = MLAN_OID_BSS_MODE;
+ req->req_id = MLAN_IOCTL_BSS;
+ req->action = MLAN_ACT_GET;
+
+ /* Send IOCTL request to MLAN */
+ status = woal_request_ioctl(priv, req, wait_option);
+ if (status == MLAN_STATUS_SUCCESS) {
+ switch (bss->param.bss_mode) {
+ case MLAN_BSS_MODE_INFRA:
+ mode = IW_MODE_INFRA;
+ break;
+ case MLAN_BSS_MODE_IBSS:
+ mode = IW_MODE_ADHOC;
+ break;
+ default:
+ mode = IW_MODE_AUTO;
+ break;
+ }
+ }
+ done:
+ if (req && (status != MLAN_STATUS_PENDING))
+ kfree(req);
+ LEAVE();
+ return mode;
+}
+
+/**
+ * @brief Get statistics information
+ *
+ * @param priv A pointer to moal_private structure
+ * @param wait_option Wait option
+ * @param stats A pointer to mlan_ds_get_stats structure
+ *
+ * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail
+ */
+mlan_status
+woal_get_stats_info(moal_private * priv, t_u8 wait_option,
+ mlan_ds_get_stats * stats)
+{
+ int ret = 0;
+ mlan_ds_get_info *info = NULL;
+ mlan_ioctl_req *req = NULL;
+ mlan_status status = MLAN_STATUS_SUCCESS;
+ ENTER();
+
+ /* Allocate an IOCTL request buffer */
+ req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_get_info));
+ if (req == NULL) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ /* Fill request buffer */
+ info = (mlan_ds_get_info *) req->pbuf;
+ info->sub_command = MLAN_OID_GET_STATS;
+ req->req_id = MLAN_IOCTL_GET_INFO;
+ req->action = MLAN_ACT_GET;
+
+ /* Send IOCTL request to MLAN */
+ status = woal_request_ioctl(priv, req, wait_option);
+ if (status == MLAN_STATUS_SUCCESS) {
+ if (stats)
+ memcpy(stats, &info->param.stats, sizeof(mlan_ds_get_stats));
+ priv->w_stats.discard.fragment = info->param.stats.fcs_error;
+ priv->w_stats.discard.retries = info->param.stats.retry;
+ priv->w_stats.discard.misc = info->param.stats.ack_failure;
+ }
+ done:
+ if (req && (status != MLAN_STATUS_PENDING))
+ kfree(req);
+ LEAVE();
+ return status;
+}
+
+/**
+ * @brief Get data rates
+ *
+ * @param priv A pointer to moal_private structure
+ * @param wait_option Wait option
+ * @param m_rates A pointer to moal_802_11_rates structure
+ *
+ * @return MLAN_STATUS_SUCCESS/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)
+{
+ int ret = 0;
+ mlan_ds_rate *rate = NULL;
+ mlan_ioctl_req *req = NULL;
+ mlan_status status = MLAN_STATUS_SUCCESS;
+ ENTER();
+
+ /* Allocate an IOCTL request buffer */
+ req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_rate));
+ if (req == NULL) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ /* Fill request buffer */
+ rate = (mlan_ds_rate *) req->pbuf;
+ rate->sub_command = MLAN_OID_SUPPORTED_RATES;
+ req->req_id = MLAN_IOCTL_RATE;
+ req->action = MLAN_ACT_GET;
+
+ /* Send IOCTL request to MLAN */
+ status = woal_request_ioctl(priv, req, wait_option);
+ if (status == MLAN_STATUS_SUCCESS) {
+ if (m_rates)
+ m_rates->num_of_rates =
+ woal_copy_rates(m_rates->rates, m_rates->num_of_rates,
+ rate->param.rates, MLAN_SUPPORTED_RATES);
+ }
+ done:
+ if (req && (status != MLAN_STATUS_PENDING))
+ kfree(req);
+ LEAVE();
+ return status;
+}
+
+/**
+ * @brief Get channel list
+ *
+ * @param priv A pointer to moal_private structure
+ * @param wait_option Wait option
+ * @param chan_list A pointer to mlan_chan_list structure
+ *
+ * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail
+ */
+mlan_status
+woal_get_channel_list(moal_private * priv, t_u8 wait_option,
+ mlan_chan_list * chan_list)
+{
+ int ret = 0;
+ mlan_ds_bss *bss = NULL;
+ mlan_ioctl_req *req = NULL;
+ mlan_status status = MLAN_STATUS_SUCCESS;
+ ENTER();
+
+ /* Allocate an IOCTL request buffer */
+ req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss));
+ if (req == NULL) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ /* Fill request buffer */
+ bss = (mlan_ds_bss *) req->pbuf;
+ bss->sub_command = MLAN_OID_BSS_CHANNEL_LIST;
+ req->req_id = MLAN_IOCTL_BSS;
+ req->action = MLAN_ACT_GET;
+
+ /* Send IOCTL request to MLAN */
+ status = woal_request_ioctl(priv, req, wait_option);
+ if (status == MLAN_STATUS_SUCCESS) {
+ if (chan_list) {
+ memcpy(chan_list, &bss->param.chanlist, sizeof(mlan_chan_list));
+ }
+ }
+ done:
+ if (req && (status != MLAN_STATUS_PENDING))
+ kfree(req);
+ LEAVE();
+ return status;
+}
+
+/**
+ * @brief 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;
+}