diff options
Diffstat (limited to 'drivers/net/wireless/sd8797/mlinux/moal_eth_ioctl.c')
-rw-r--r-- | drivers/net/wireless/sd8797/mlinux/moal_eth_ioctl.c | 1143 |
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; +} |