summaryrefslogtreecommitdiff
path: root/drivers/net/wireless/sd8797/mlinux/moal_eth_ioctl.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/sd8797/mlinux/moal_eth_ioctl.c')
-rw-r--r--drivers/net/wireless/sd8797/mlinux/moal_eth_ioctl.c1143
1 files changed, 1143 insertions, 0 deletions
diff --git a/drivers/net/wireless/sd8797/mlinux/moal_eth_ioctl.c b/drivers/net/wireless/sd8797/mlinux/moal_eth_ioctl.c
new file mode 100644
index 000000000000..984400cd532f
--- /dev/null
+++ b/drivers/net/wireless/sd8797/mlinux/moal_eth_ioctl.c
@@ -0,0 +1,1143 @@
+/** @file moal_eth_ioctl.c
+ *
+ * @brief This file contains private ioctl functions
+ *
+ * Copyright (C) 2012, 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:
+ 01/05/2012: initial version
+************************************************************************/
+
+#include "moal_main.h"
+#include "moal_eth_ioctl.h"
+#include "mlan_ioctl.h"
+#if defined(STA_WEXT) || defined(UAP_WEXT)
+#include "moal_priv.h"
+#endif
+
+#if defined(STA_CFG80211) && defined(UAP_CFG80211)
+#include "moal_cfg80211.h"
+#endif
+#ifdef UAP_SUPPORT
+#include "moal_uap.h"
+#endif
+/********************************************************
+ Local Variables
+********************************************************/
+
+/** Marvell private command identifier string */
+#define CMD_MARVELL "MRVL_CMD"
+
+/** Private command: Version */
+#define PRIV_CMD_VERSION "version"
+/** Private command: Band cfg */
+#define PRIV_CMD_BANDCFG "bandcfg"
+/** Private command: Host cmd */
+#define PRIV_CMD_HOSTCMD "hostcmd"
+/** Private command: HT Tx Cfg */
+#define PRIV_CMD_HTTXCFG "httxcfg"
+#define PRIV_CMD_DATARATE "getdatarate"
+#define PRIV_CMD_TXRATECFG "txratecfg"
+
+/** 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
+********************************************************/
+
+/********************************************************
+ Local Functions
+********************************************************/
+/**
+ * @brief Parse a string to extract arguments
+ *
+ * @param pos Pointer to the arguments string
+ * @param data Pointer to the arguments buffer
+ * @param len Pointer to the number of arguments extracted
+ *
+ * @return MLAN_STATUS_SUCCESS
+ *
+ * N.B. No boundary check is done on 'data'. The caller must ensure there
+ * are enough space for all extracted arguments.
+ */
+mlan_status
+parse_arguments(t_u8 * pos, int *data, int *user_data_len)
+{
+ unsigned int i, j, k;
+ char cdata[10];
+ int is_hex = 0;
+
+ memset(cdata, 0, sizeof(cdata));
+ for (i = 0, j = 0, k = 0; i <= strlen(pos); i++) {
+ if ((k == 0) && (i <= (strlen(pos) - 2))) {
+ if ((pos[i] == '0') && (pos[i + 1] == 'x')) {
+ is_hex = 1;
+ i = i + 2;
+ }
+ }
+ if (pos[i] == '\0') {
+ if (is_hex) {
+ data[j] = woal_atox(cdata);
+ is_hex = 0;
+ } else {
+ woal_atoi(&data[j], cdata);
+ }
+ j++;
+ (*user_data_len)++;
+ k = 0;
+ memset(cdata, 0, sizeof(char) * 4);
+ break;
+ } else if (pos[i] == ' ') {
+ if (is_hex) {
+ data[j] = woal_atox(cdata);
+ is_hex = 0;
+ } else {
+ woal_atoi(&data[j], cdata);
+ }
+ j++;
+ (*user_data_len)++;
+ k = 0;
+ memset(cdata, 0, sizeof(char) * 4);
+ } else {
+ cdata[k] = pos[i];
+ k++;
+ }
+ }
+
+ return MLAN_STATUS_SUCCESS;
+}
+
+#if defined(STA_CFG80211) && defined(UAP_CFG80211)
+/**
+ * @brief Set wps & p2p ie in AP mode
+ *
+ * @param priv Pointer to priv stucture
+ * @param ie Pointer to ies data
+ * @param len Length of data
+ *
+ * @return MLAN_STATUS_SUCCESS -- success, otherwise fail
+ */
+mlan_status
+woal_set_ap_wps_p2p_ie(moal_private * priv, t_u8 * ie, size_t len)
+{
+ mlan_status ret = MLAN_STATUS_SUCCESS;
+ t_u8 *pos = ie;
+ t_u32 ie_len;
+
+ ENTER();
+
+ ie_len = len - 2;
+ if (ie_len <= 0 || ie_len > MAX_IE_SIZE) {
+ PRINTM(MERROR, "IE len error: %d\n", ie_len);
+ ret = -EFAULT;
+ goto done;
+ }
+
+ /* Android cmd format: "SET_AP_WPS_P2P_IE 1" -- beacon IE
+ "SET_AP_WPS_P2P_IE 2" -- proberesp IE "SET_AP_WPS_P2P_IE 4" -- assocresp
+ IE */
+ if (*pos == '1') {
+ PRINTM(MIOCTL, "Ignore set beacon ie\n");
+ goto done;
+ } else if (*pos == '2') {
+ /* set the probe resp ies */
+ pos += 2;
+ if (MLAN_STATUS_SUCCESS != woal_cfg80211_mgmt_frame_ie(priv, NULL,
+ 0, pos, ie_len,
+ NULL, 0, NULL, 0,
+ MGMT_MASK_PROBE_RESP))
+ {
+ PRINTM(MERROR, "Failed to set probe resp ie\n");
+ ret = -EFAULT;
+ goto done;
+ }
+ } else if (*pos == '4') {
+ /* set the assoc resp ies */
+ pos += 2;
+ if (MLAN_STATUS_SUCCESS != woal_cfg80211_mgmt_frame_ie(priv, NULL,
+ 0, NULL, 0, pos,
+ ie_len, NULL, 0,
+ MGMT_MASK_ASSOC_RESP))
+ {
+ PRINTM(MERROR, "Failed to set assoc resp ie\n");
+ ret = -EFAULT;
+ goto done;
+ }
+ }
+
+ done:
+ LEAVE();
+ return ret;
+}
+#endif
+
+/**
+ * @brief Get Driver Version
+ *
+ * @param priv A pointer to moal_private structure
+ * @param respbuf A pointer to response buffer
+ * @param respbuflen Available length of response buffer
+ *
+ * @return Number of bytes written, negative for failure.
+ */
+int
+woal_get_priv_driver_version(moal_private * priv, t_u8 * respbuf,
+ t_u32 respbuflen)
+{
+ int len = 0, ret = -1;
+ char buf[MLAN_MAX_VER_STR_LEN];
+
+ ENTER();
+
+ if (!respbuf) {
+ LEAVE();
+ return 0;
+ }
+
+ memset(buf, 0, sizeof(buf));
+
+ /* Get version string to local buffer */
+ woal_get_version(priv->phandle, buf, sizeof(buf) - 1);
+ len = strlen(buf);
+
+ if (len) {
+ /* Copy back the retrieved version string */
+ PRINTM(MINFO, "MOAL VERSION: %s\n", buf);
+ ret = MIN(len, (respbuflen - 1));
+ memcpy(respbuf, buf, ret);
+ } else {
+ ret = -1;
+ PRINTM(MERROR, "Get version failed!\n");
+ }
+
+ LEAVE();
+ return ret;
+}
+
+#ifdef WIFI_DIRECT_SUPPORT
+/**
+ * @brief Hostcmd interface from application
+ *
+ * @param priv A pointer to moal_private structure
+ * @param respbuf A pointer to response buffer
+ * @param respbuflen Available length of response buffer
+ *
+ * @return Number of bytes written, negative for failure.
+ */
+int
+woal_priv_hostcmd(moal_private * priv, t_u8 * respbuf, t_u32 respbuflen)
+{
+ int ret = 0;
+ t_u8 *data_ptr;
+ t_u32 buf_len = 0;
+ HostCmd_Header cmd_header;
+ mlan_ioctl_req *req = NULL;
+ mlan_ds_misc_cfg *misc_cfg = NULL;
+
+ ENTER();
+
+ data_ptr = respbuf + (strlen(CMD_MARVELL) + strlen(PRIV_CMD_HOSTCMD));
+ buf_len = *((t_u32 *) data_ptr);
+ memcpy(&cmd_header, data_ptr + sizeof(buf_len), sizeof(HostCmd_Header));
+
+ PRINTM(MINFO, "Host command len = %d\n", woal_le16_to_cpu(cmd_header.size));
+ if (woal_le16_to_cpu(cmd_header.size) > MLAN_SIZE_OF_CMD_BUFFER) {
+ LEAVE();
+ return -EINVAL;
+ }
+
+ req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg));
+ if (req == NULL) {
+ ret = -ENOMEM;
+ goto error;
+ }
+ misc_cfg = (mlan_ds_misc_cfg *) req->pbuf;
+ misc_cfg->sub_command = MLAN_OID_MISC_HOST_CMD;
+ req->req_id = MLAN_IOCTL_MISC_CFG;
+ req->action = MLAN_ACT_SET;
+ misc_cfg->param.hostcmd.len = woal_le16_to_cpu(cmd_header.size);
+ /* get the whole command */
+ memcpy(misc_cfg->param.hostcmd.cmd, data_ptr + sizeof(buf_len),
+ misc_cfg->param.hostcmd.len);
+
+ if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
+ ret = -EFAULT;
+ goto error;
+ }
+ sprintf(respbuf, "OK");
+ ret = 3;
+
+ error:
+ if (req)
+ kfree(req);
+
+ LEAVE();
+ return ret;
+}
+#endif
+
+/**
+ * @brief Set/Get Band and Adhoc-band setting
+ *
+ * @param priv A pointer to moal_private structure
+ * @param respbuf A pointer to response buffer
+ * @param respbuflen Available length of response buffer
+ *
+ * @return Number of bytes written, negative for failure.
+ */
+int
+woal_setget_priv_bandcfg(moal_private * priv, t_u8 * respbuf, t_u32 respbuflen)
+{
+ int ret = 0;
+ unsigned int i;
+ int data[4];
+ int user_data_len = 0;
+ 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;
+ mlan_ds_band_cfg *band_cfg = NULL;
+
+ ENTER();
+
+ if (strlen(respbuf) == (strlen(CMD_MARVELL) + strlen(PRIV_CMD_BANDCFG))) {
+ /* GET operation */
+ user_data_len = 0;
+ } else {
+ /* SET operation */
+ memset((char *) data, 0, sizeof(data));
+ parse_arguments(respbuf + strlen(CMD_MARVELL) +
+ strlen(PRIV_CMD_BANDCFG), data, &user_data_len);
+ }
+
+ 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 (user_data_len == 0) {
+ /* Get config_bands, adhoc_start_band and adhoc_channel values from
+ MLAN */
+ req->action = MLAN_ACT_GET;
+ } else {
+ /* To support only <b/bg/bgn/n/aac/gac> */
+ 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];
+ /* sanity test */
+ 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;
+ }
+
+ band_cfg = (mlan_ds_band_cfg *) respbuf;
+
+ memcpy(band_cfg, &radio_cfg->param.band_cfg, sizeof(mlan_ds_band_cfg));
+
+ ret = sizeof(mlan_ds_band_cfg);
+
+ error:
+ if (req)
+ kfree(req);
+
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief Set/Get 11n configurations
+ *
+ * @param priv A pointer to moal_private structure
+ * @param respbuf A pointer to response buffer
+ * @param respbuflen Available length of response buffer
+ *
+ * @return Number of bytes written, negative for failure.
+ */
+int
+woal_setget_priv_httxcfg(moal_private * priv, t_u8 * respbuf, t_u32 respbuflen)
+{
+ int data[2];
+ mlan_ioctl_req *req = NULL;
+ mlan_ds_11n_cfg *cfg_11n = NULL;
+ int ret = 0;
+ int user_data_len = 0;
+
+ ENTER();
+
+ if (strlen(respbuf) == (strlen(CMD_MARVELL) + strlen(PRIV_CMD_HTTXCFG))) {
+ /* GET operation */
+ user_data_len = 0;
+ } else {
+ /* SET operation */
+ memset((char *) data, 0, sizeof(data));
+ parse_arguments(respbuf + strlen(CMD_MARVELL) +
+ strlen(PRIV_CMD_HTTXCFG), data, &user_data_len);
+ }
+
+ if (user_data_len > 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 (user_data_len == 0) {
+ /* Get 11n tx parameters from MLAN */
+ req->action = MLAN_ACT_GET;
+ cfg_11n->param.tx_cfg.misc_cfg = BAND_SELECT_BG;
+ } else {
+ 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 (user_data_len == 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) {
+ user_data_len = 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]) {
+ user_data_len = 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]);
+ }
+
+ sprintf(respbuf, "0x%x", data[0]);
+ ret = strlen(respbuf) + 1;
+
+ done:
+ if (req)
+ kfree(req);
+ LEAVE();
+ return ret;
+
+}
+
+/**
+ * @brief Set/Get 11AC configurations
+ *
+ * @param priv A pointer to moal_private structure
+ * @param respbuf A pointer to response buffer
+ * @param respbuflen Available length of response buffer
+ *
+ * @return Number of bytes written, negative for failure.
+ */
+int
+woal_get_priv_datarate(moal_private * priv, t_u8 * respbuf, t_u32 respbuflen)
+{
+ mlan_ioctl_req *req = NULL;
+ mlan_ds_rate *rate = NULL;
+ mlan_data_rate *data_rate = NULL;
+ int ret = 0;
+
+ 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;
+ }
+
+ data_rate = (mlan_data_rate *) respbuf;
+
+ memcpy(data_rate, &rate->param.data_rate, sizeof(mlan_data_rate));
+
+ ret = sizeof(mlan_data_rate);
+
+ done:
+ if (req)
+ kfree(req);
+ LEAVE();
+ return ret;
+
+}
+
+/**
+ * @brief Set/Get tx rate configurations
+ *
+ * @param priv A pointer to moal_private structure
+ * @param respbuf A pointer to response buffer
+ * @param respbuflen Available length of response buffer
+ *
+ * @return Number of bytes written, negative for failure.
+ */
+int
+woal_setget_priv_txratecfg(moal_private * priv, t_u8 * respbuf,
+ t_u32 respbuflen)
+{
+ t_u32 data[3];
+ mlan_ioctl_req *req = NULL;
+ mlan_ds_rate *rate = NULL;
+ woal_tx_rate_cfg *ratecfg = NULL;
+ int ret = 0;
+ int user_data_len = 0;
+
+ ENTER();
+
+ if (strlen(respbuf) == (strlen(CMD_MARVELL) + strlen(PRIV_CMD_TXRATECFG))) {
+ /* GET operation */
+ user_data_len = 0;
+ } else {
+ /* SET operation */
+ memset((char *) data, 0, sizeof(data));
+ parse_arguments(respbuf + strlen(CMD_MARVELL) +
+ strlen(PRIV_CMD_TXRATECFG), data, &user_data_len);
+ }
+
+ if (user_data_len >= 4) {
+ PRINTM(MERROR, "Invalid number of arguments\n");
+ ret = -EINVAL;
+ goto done;
+ }
+
+ req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_rate));
+ if (req == NULL) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ req->req_id = MLAN_IOCTL_RATE;
+ rate = (mlan_ds_rate *) req->pbuf;
+ rate->sub_command = MLAN_OID_RATE_CFG;
+ rate->param.rate_cfg.rate_type = MLAN_RATE_INDEX;
+
+ if (user_data_len == 0) {
+ /* Get operation */
+ req->action = MLAN_ACT_GET;
+ } else {
+ /* Set operation */
+ req->action = MLAN_ACT_SET;
+ /* format */
+ if ((data[0] != AUTO_RATE) && (data[0] >= 3)) {
+ PRINTM(MERROR, "Invalid format selection\n");
+ ret = -EINVAL;
+ goto done;
+ }
+ if (data[0] == AUTO_RATE) {
+ /* auto */
+ rate->param.rate_cfg.is_rate_auto = 1;
+ } else {
+ /* fixed rate */
+ PRINTM(MINFO, "SET: txratefg format: 0x%x\n", data[0]);
+ if ((data[0] != AUTO_RATE) && (data[0] > MLAN_RATE_FORMAT_HT)
+ ) {
+ PRINTM(MERROR, "Invalid format selection\n");
+ ret = -EINVAL;
+ goto done;
+ }
+ }
+
+ if ((user_data_len >= 2) && (data[0] != AUTO_RATE)) {
+ PRINTM(MINFO, "SET: txratefg index: 0x%x\n", data[1]);
+ /* sanity check */
+ if (((data[0] == MLAN_RATE_FORMAT_LG) &&
+ (data[1] > MLAN_RATE_INDEX_OFDM7))
+ || ((data[0] == MLAN_RATE_FORMAT_HT) && (data[1] != 32) &&
+ (data[1] > 15))
+ ) {
+ PRINTM(MERROR, "Invalid index selection\n");
+ ret = -EINVAL;
+ goto done;
+ }
+
+ PRINTM(MINFO, "SET: txratefg index: 0x%x\n", data[1]);
+ rate->param.rate_cfg.rate = data[1];
+
+ if (data[0] == MLAN_RATE_FORMAT_HT) {
+ rate->param.rate_cfg.rate = data[1] + MLAN_RATE_INDEX_MCS0;
+ }
+ }
+
+ }
+
+ if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) {
+ ret = -EFAULT;
+ goto done;
+ }
+
+ ratecfg = (woal_tx_rate_cfg *) respbuf;
+ if (rate->param.rate_cfg.is_rate_auto == MTRUE) {
+ ratecfg->rate_format = 0xFF;
+ } else {
+ /* fixed rate */
+ if (rate->param.rate_cfg.rate < MLAN_RATE_INDEX_MCS0) {
+ ratecfg->rate_format = MLAN_RATE_FORMAT_LG;
+ ratecfg->rate_index = rate->param.rate_cfg.rate;
+ } else {
+ ratecfg->rate_format = MLAN_RATE_FORMAT_HT;
+ ratecfg->rate_index =
+ rate->param.rate_cfg.rate - MLAN_RATE_INDEX_MCS0;
+ }
+ }
+
+ ret = sizeof(woal_tx_rate_cfg);
+
+ done:
+ if (req)
+ kfree(req);
+ LEAVE();
+ return ret;
+
+}
+
+/**
+ * @brief Set priv command for Android
+ * @param dev A pointer to net_device structure
+ * @param req A pointer to ifreq structure
+ *
+ * @return 0 --success, otherwise fail
+ */
+int
+woal_android_priv_cmd(struct net_device *dev, struct ifreq *req)
+{
+ int ret = 0;
+ android_wifi_priv_cmd priv_cmd;
+ moal_private *priv = (moal_private *) netdev_priv(dev);
+ char *buf = NULL;
+ char *pdata;
+#ifdef STA_SUPPORT
+ int power_mode = 0;
+ int band = 0;
+ char *pband = NULL;
+ mlan_bss_info bss_info;
+ mlan_ds_get_signal signal;
+ mlan_rate_cfg_t rate;
+ t_u8 country_code[COUNTRY_CODE_LEN];
+#endif
+ int len = 0;
+
+ ENTER();
+ if (copy_from_user(&priv_cmd, req->ifr_data, sizeof(android_wifi_priv_cmd))) {
+ ret = -EFAULT;
+ goto done;
+ }
+ buf = kzalloc(priv_cmd.total_len, GFP_KERNEL);
+ if (!buf) {
+ PRINTM(MERROR, "%s: failed to allocate memory\n", __FUNCTION__);
+ ret = -ENOMEM;
+ goto done;
+ }
+ if (copy_from_user(buf, priv_cmd.buf, priv_cmd.total_len)) {
+ ret = -EFAULT;
+ goto done;
+ }
+
+ PRINTM(MIOCTL, "Android priv cmd: [%s] on [%s]\n", buf, req->ifr_name);
+
+ if (strncmp(buf, CMD_MARVELL, strlen(CMD_MARVELL)) == 0) {
+ /* This command has come from mlanutl app */
+
+ /* Check command */
+ if (strnicmp
+ (buf + strlen(CMD_MARVELL), PRIV_CMD_VERSION,
+ strlen(PRIV_CMD_VERSION)) == 0) {
+ /* Get version */
+ len = woal_get_priv_driver_version(priv, buf, priv_cmd.total_len);
+ goto handled;
+ } else
+ if (strnicmp
+ (buf + strlen(CMD_MARVELL), PRIV_CMD_BANDCFG,
+ strlen(PRIV_CMD_BANDCFG)) == 0) {
+ /* Set/Get band configuration */
+ len = woal_setget_priv_bandcfg(priv, buf, priv_cmd.total_len);
+ goto handled;
+#ifdef WIFI_DIRECT_SUPPORT
+ } else
+ if (strnicmp
+ (buf + strlen(CMD_MARVELL), PRIV_CMD_HOSTCMD,
+ strlen(PRIV_CMD_HOSTCMD)) == 0) {
+ /* hostcmd configuration */
+ len = woal_priv_hostcmd(priv, buf, priv_cmd.total_len);
+ goto handled;
+#endif
+ } else
+ if (strnicmp
+ (buf + strlen(CMD_MARVELL), PRIV_CMD_HTTXCFG,
+ strlen(PRIV_CMD_HTTXCFG)) == 0) {
+ /* Set/Get HT Tx configuration */
+ len = woal_setget_priv_httxcfg(priv, buf, priv_cmd.total_len);
+ goto handled;
+ } else
+ if (strnicmp
+ (buf + strlen(CMD_MARVELL), PRIV_CMD_DATARATE,
+ strlen(PRIV_CMD_DATARATE)) == 0) {
+ /* Get data rate */
+ len = woal_get_priv_datarate(priv, buf, priv_cmd.total_len);
+ goto handled;
+ } else
+ if (strnicmp
+ (buf + strlen(CMD_MARVELL), PRIV_CMD_TXRATECFG,
+ strlen(PRIV_CMD_TXRATECFG)) == 0) {
+ /* Set/Get tx rate cfg */
+ len = woal_setget_priv_txratecfg(priv, buf, priv_cmd.total_len);
+ goto handled;
+ } else {
+ /* Fall through, after stripping off the custom header */
+ buf += strlen(CMD_MARVELL);
+ }
+ }
+#ifdef STA_SUPPORT
+ if (strncmp(buf, "RSSI", strlen("RSSI")) == 0) {
+ if (MLAN_STATUS_SUCCESS != woal_get_bss_info(priv,
+ MOAL_IOCTL_WAIT,
+ &bss_info)) {
+ ret = -EFAULT;
+ goto done;
+ }
+ if (bss_info.media_connected) {
+ if (MLAN_STATUS_SUCCESS != woal_get_signal_info(priv,
+ MOAL_IOCTL_WAIT,
+ &signal)) {
+ ret = -EFAULT;
+ goto done;
+ }
+ len = sprintf(buf, "%s rssi %d\n", bss_info.ssid.ssid,
+ signal.bcn_rssi_avg) + 1;
+ } else {
+ len = sprintf(buf, "OK\n") + 1;
+ }
+ } else if (strncmp(buf, "LINKSPEED", strlen("LINKSPEED")) == 0) {
+ if (MLAN_STATUS_SUCCESS != woal_set_get_data_rate(priv, MLAN_ACT_GET,
+ &rate)) {
+ ret = -EFAULT;
+ goto done;
+ }
+ PRINTM(MIOCTL, "tx rate=%d\n", (int) rate.rate);
+ len =
+ sprintf(buf, "LinkSpeed %d\n", (int) (rate.rate * 500000 / 1000000))
+ + 1;
+ } else
+#endif
+ if (strncmp(buf, "MACADDR", strlen("MACADDR")) == 0) {
+ len = sprintf(buf, "Macaddr = %02X:%02X:%02X:%02X:%02X:%02X\n",
+ priv->current_addr[0], priv->current_addr[1],
+ priv->current_addr[2], priv->current_addr[3],
+ priv->current_addr[4], priv->current_addr[5]) + 1;
+ }
+#ifdef STA_SUPPORT
+ else if (strncmp(buf, "GETPOWER", strlen("GETPOWER")) == 0) {
+ if (MLAN_STATUS_SUCCESS != woal_get_powermode(priv, &power_mode)) {
+ ret = -EFAULT;
+ goto done;
+ }
+ len = sprintf(buf, "powermode = %d\n", power_mode) + 1;
+ } else if (strncmp(buf, "SCAN-ACTIVE", strlen("SCAN-ACTIVE")) == 0) {
+ if (MLAN_STATUS_SUCCESS != woal_set_scan_type(priv,
+ MLAN_SCAN_TYPE_ACTIVE)) {
+ ret = -EFAULT;
+ goto done;
+ }
+ priv->scan_type = MLAN_SCAN_TYPE_ACTIVE;
+ PRINTM(MIOCTL, "Set Active Scan\n");
+ len = sprintf(buf, "OK\n") + 1;
+ } else if (strncmp(buf, "SCAN-PASSIVE", strlen("SCAN-PASSIVE")) == 0) {
+ if (MLAN_STATUS_SUCCESS != woal_set_scan_type(priv,
+ MLAN_SCAN_TYPE_PASSIVE)) {
+ ret = -EFAULT;
+ goto done;
+ }
+ priv->scan_type = MLAN_SCAN_TYPE_PASSIVE;
+ PRINTM(MIOCTL, "Set Passive Scan\n");
+ len = sprintf(buf, "OK\n") + 1;
+ } else if (strncmp(buf, "POWERMODE", strlen("POWERMODE")) == 0) {
+ pdata = buf + strlen("POWERMODE") + 1;
+ if (MLAN_STATUS_SUCCESS != woal_set_powermode(priv, pdata)) {
+ ret = -EFAULT;
+ goto done;
+ }
+ len = sprintf(buf, "OK\n") + 1;
+ } else if (strncmp(buf, "COUNTRY", strlen("COUNTRY")) == 0) {
+ memset(country_code, 0, sizeof(country_code));
+ memcpy(country_code, buf + strlen("COUNTRY") + 1,
+ strlen(buf) - strlen("COUNTRY") - 1);
+ PRINTM(MIOCTL, "Set COUNTRY %s\n", country_code);
+ if (MLAN_STATUS_SUCCESS != woal_set_region_code(priv, country_code)) {
+ ret = -EFAULT;
+ goto done;
+ }
+ len = sprintf(buf, "OK\n") + 1;
+ } else if (memcmp(buf, WEXT_CSCAN_HEADER, WEXT_CSCAN_HEADER_SIZE) == 0) {
+ PRINTM(MIOCTL, "Set Combo Scan\n");
+ if (MLAN_STATUS_SUCCESS != woal_set_combo_scan(priv, buf,
+ priv_cmd.total_len)) {
+ ret = -EFAULT;
+ goto done;
+ }
+ len = sprintf(buf, "OK\n") + 1;
+ } else if (strncmp(buf, "GETBAND", strlen("GETBAND")) == 0) {
+ if (MLAN_STATUS_SUCCESS != woal_get_band(priv, &band)) {
+ ret = -EFAULT;
+ goto done;
+ }
+ len = sprintf(buf, "Band %d\n", band) + 1;
+ } else if (strncmp(buf, "SETBAND", strlen("SETBAND")) == 0) {
+ pband = buf + strlen("SETBAND") + 1;
+ if (MLAN_STATUS_SUCCESS != woal_set_band(priv, pband)) {
+ ret = -EFAULT;
+ goto done;
+ }
+ len = sprintf(buf, "OK\n") + 1;
+ }
+#endif
+ else if (strncmp(buf, "START", strlen("START")) == 0) {
+ len = sprintf(buf, "OK\n") + 1;
+ } else if (strncmp(buf, "STOP", strlen("STOP")) == 0) {
+ len = sprintf(buf, "OK\n") + 1;
+ }
+#ifdef UAP_SUPPORT
+ else if (strncmp(buf, "AP_BSS_START", strlen("AP_BSS_START")) == 0) {
+ if ((ret == woal_uap_bss_ctrl(priv, MOAL_IOCTL_WAIT, UAP_BSS_START)))
+ goto done;
+ len = sprintf(buf, "OK\n") + 1;
+ } else if (strncmp(buf, "AP_BSS_STOP", strlen("AP_BSS_STOP")) == 0) {
+ if ((ret == woal_uap_bss_ctrl(priv, MOAL_IOCTL_WAIT, UAP_BSS_STOP)))
+ goto done;
+ len = sprintf(buf, "OK\n") + 1;
+ } else if (strncmp(buf, "AP_SET_CFG", strlen("AP_SET_CFG")) == 0) {
+ pdata = buf + strlen("AP_SET_CFG") + 1;
+ if ((ret =
+ woal_uap_set_ap_cfg(priv, pdata,
+ priv_cmd.used_len - strlen("AP_SET_CFG") - 1)))
+ goto done;
+ len = sprintf(buf, "OK\n") + 1;
+ } else if (strncmp(buf, "WL_FW_RELOAD", strlen("WL_FW_RELOAD")) == 0) {
+ len = sprintf(buf, "OK\n") + 1;
+ } else if (strncmp(buf, "AP_GET_STA_LIST", strlen("AP_GET_STA_LIST")) == 0) {
+ // TODO Add STA list support
+ len = sprintf(buf, "OK\n") + 1;
+ }
+#endif
+ else if (strncmp(buf, "SETSUSPENDOPT", strlen("SETSUSPENDOPT")) == 0) {
+ /* it will be done by GUI */
+ len = sprintf(buf, "OK\n") + 1;
+ } else if (strncmp(buf, "BTCOEXMODE", strlen("BTCOEXMODE")) == 0) {
+ len = sprintf(buf, "OK\n") + 1;
+ } else if (strncmp(buf, "BTCOEXSCAN-START", strlen("BTCOEXSCAN-START")) ==
+ 0) {
+ len = sprintf(buf, "OK\n") + 1;
+ } else if (strncmp(buf, "BTCOEXSCAN-STOP", strlen("BTCOEXSCAN-STOP")) == 0) {
+ len = sprintf(buf, "OK\n") + 1;
+ } else if (strncmp(buf, "BTCOEXSCAN-START", strlen("BTCOEXSCAN-START")) ==
+ 0) {
+ len = sprintf(buf, "OK\n") + 1;
+ } else if (strncmp(buf, "BTCOEXSCAN-STOP", strlen("BTCOEXSCAN-STOP")) == 0) {
+ len = sprintf(buf, "OK\n") + 1;
+ }
+#ifdef STA_SUPPORT
+ else if (strncmp(buf, "BGSCAN-START", strlen("BGSCAN-START")) == 0) {
+ len = sprintf(buf, "OK\n") + 1;
+ } else if (strncmp(buf, "BGSCAN-CONFIG", strlen("BGSCAN-CONFIG")) == 0) {
+ if (MLAN_STATUS_SUCCESS !=
+ woal_set_bg_scan(priv, buf, priv_cmd.total_len)) {
+ ret = -EFAULT;
+ goto done;
+ }
+ priv->bg_scan_start = MTRUE;
+ priv->bg_scan_reported = MFALSE;
+ len = sprintf(buf, "OK\n") + 1;
+ } else if (strncmp(buf, "BGSCAN-STOP", strlen("BGSCAN-STOP")) == 0) {
+ if (MLAN_STATUS_SUCCESS != woal_stop_bg_scan(priv)) {
+ ret = -EFAULT;
+ goto done;
+ }
+ priv->bg_scan_start = MFALSE;
+ priv->bg_scan_reported = MFALSE;
+ len = sprintf(buf, "OK\n") + 1;
+ } else if (strncmp(buf, "RXFILTER-START", strlen("RXFILTER-START")) == 0) {
+#ifdef MEF_CFG_RX_FILTER
+ if ((ret = woal_set_rxfilter(priv, MTRUE))) {
+ goto done;
+ }
+#endif
+ len = sprintf(buf, "OK\n") + 1;
+ } else if (strncmp(buf, "RXFILTER-STOP", strlen("RXFILTER-STOP")) == 0) {
+#ifdef MEF_CFG_RX_FILTER
+ if ((ret = woal_set_rxfilter(priv, MFALSE))) {
+ goto done;
+ }
+#endif
+ len = sprintf(buf, "OK\n") + 1;
+ } else if (strncmp(buf, "RXFILTER-ADD", strlen("RXFILTER-ADD")) == 0) {
+ pdata = buf + strlen("RXFILTER-ADD") + 1;
+ if (MLAN_STATUS_SUCCESS != woal_add_rxfilter(priv, pdata)) {
+ ret = -EFAULT;
+ goto done;
+ }
+ len = sprintf(buf, "OK\n") + 1;
+ } else if (strncmp(buf, "RXFILTER-REMOVE", strlen("RXFILTER-REMOVE")) == 0) {
+ pdata = buf + strlen("RXFILTER-REMOVE") + 1;
+ if (MLAN_STATUS_SUCCESS != woal_remove_rxfilter(priv, pdata)) {
+ ret = -EFAULT;
+ goto done;
+ }
+ len = sprintf(buf, "OK\n") + 1;
+ } else if (strncmp(buf, "QOSINFO", strlen("QOSINFO")) == 0) {
+ pdata = buf + strlen("QOSINFO") + 1;
+ if (MLAN_STATUS_SUCCESS != woal_set_qos_cfg(priv, pdata)) {
+ ret = -EFAULT;
+ goto done;
+ }
+ len = sprintf(buf, "OK\n") + 1;
+ } else if (strncmp(buf, "SLEEPPD", strlen("SLEEPPD")) == 0) {
+ pdata = buf + strlen("SLEEPPD") + 1;
+ if (MLAN_STATUS_SUCCESS != woal_set_sleeppd(priv, pdata)) {
+ ret = -EFAULT;
+ goto done;
+ }
+ len = sprintf(buf, "OK\n") + 1;
+ } else if (strncmp(buf, "SET_AP_WPS_P2P_IE",
+ strlen("SET_AP_WPS_P2P_IE")) == 0) {
+ pdata = buf + strlen("SET_AP_WPS_P2P_IE") + 1;
+ /* Android cmd format: "SET_AP_WPS_P2P_IE 1" -- beacon IE
+ "SET_AP_WPS_P2P_IE 2" -- proberesp IE "SET_AP_WPS_P2P_IE 4" --
+ assocresp IE */
+#if defined(STA_CFG80211) && defined(UAP_CFG80211)
+ if (MLAN_STATUS_SUCCESS != woal_set_ap_wps_p2p_ie(priv, (t_u8 *) pdata,
+ priv_cmd.used_len -
+ strlen
+ ("SET_AP_WPS_P2P_IE")
+ - 1)) {
+ ret = -EFAULT;
+ goto done;
+ }
+#endif
+ len = sprintf(buf, "OK\n") + 1;
+ }
+#endif
+ else if (strncmp(buf, "P2P_DEV_ADDR", strlen("P2P_DEV_ADDR")) == 0) {
+ memset(buf, 0x0, priv_cmd.total_len);
+ memcpy(buf, priv->current_addr, ETH_ALEN);
+ len = ETH_ALEN;
+ } else if (strncmp(buf, ("P2P_GET_NOA"), strlen("P2P_GET_NOA")) == 0) {
+ /* TODO Just return '\0' */
+ memset(buf, 0x0, priv_cmd.total_len);
+ *buf = 0;
+ len = 1;
+ } else {
+ PRINTM(MIOCTL, "Unknow PRIVATE command: %s, ignored\n", buf);
+ ret = -EFAULT;
+ goto done;
+ }
+
+ handled:
+ PRINTM(MIOCTL, "PRIV Command return: %s, length=%d\n", buf, len);
+
+ if (len > 0) {
+ priv_cmd.used_len = len;
+ if (copy_to_user(priv_cmd.buf, buf, len)) {
+ PRINTM(MERROR, "%s: failed to copy data to user buffer\n",
+ __FUNCTION__);
+ ret = -EFAULT;
+ }
+ } else {
+ ret = len;
+ }
+
+ done:
+ if (buf)
+ kfree(buf);
+ 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_do_ioctl(struct net_device *dev, struct ifreq *req, int cmd)
+{
+ int ret = 0;
+
+ ENTER();
+
+ PRINTM(MINFO, "woal_do_ioctl: ioctl cmd = 0x%x\n", cmd);
+ switch (cmd) {
+#ifdef WIFI_DIRECT_SUPPORT
+ case WOAL_WIFIDIRECT_HOST_CMD:
+ ret = woal_hostcmd_ioctl(dev, req);
+ break;
+#endif
+ case WOAL_CUSTOM_IE_CFG:
+ ret = woal_custom_ie_ioctl(dev, req);
+ break;
+ case WOAL_MGMT_FRAME_TX:
+ ret = woal_send_host_packet(dev, req);
+ break;
+ case WOAL_ANDROID_PRIV_CMD:
+ ret = woal_android_priv_cmd(dev, req);
+ break;
+ case WOAL_GET_BSS_TYPE:
+ ret = woal_get_bss_type(dev, req);
+ break;
+ default:
+#if defined(STA_WEXT)
+#ifdef STA_SUPPORT
+ ret = woal_wext_do_ioctl(dev, req, cmd);
+#else
+ ret = -EINVAL;
+#endif
+#else
+ ret = -EINVAL;
+#endif
+ break;
+ }
+
+ LEAVE();
+ return ret;
+}