| /** @file moal_eth_ioctl.c |
| * |
| * @brief This file contains private ioctl functions |
| * |
| * Copyright (C) 2014, 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 |
| #include "moal_sdio.h" |
| /******************************************************** |
| Local Variables |
| ********************************************************/ |
| |
| /** Bands supported in Infra mode */ |
| static t_u8 SupportedInfraBand[] = { |
| BAND_B, |
| BAND_B | BAND_G, BAND_G, |
| BAND_GN, BAND_B | BAND_G | BAND_GN, BAND_G | BAND_GN, |
| BAND_A, BAND_B | BAND_A, BAND_B | BAND_G | BAND_A, BAND_G | BAND_A, |
| BAND_A | BAND_B | BAND_G | BAND_AN | BAND_GN, |
| BAND_A | BAND_G | BAND_AN | BAND_GN, BAND_A | BAND_AN, |
| }; |
| |
| /** Bands supported in Ad-Hoc mode */ |
| static t_u8 SupportedAdhocBand[] = { |
| BAND_B, BAND_B | BAND_G, BAND_G, |
| BAND_GN, BAND_B | BAND_G | BAND_GN, BAND_G | BAND_GN, |
| BAND_A, |
| BAND_AN, BAND_A | BAND_AN, |
| }; |
| |
| /******************************************************** |
| Global Variables |
| ********************************************************/ |
| #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) |
| #ifdef UAP_SUPPORT |
| /** Network device handlers for uAP */ |
| extern const struct net_device_ops woal_uap_netdev_ops; |
| #endif |
| #ifdef STA_SUPPORT |
| /** Network device handlers for STA */ |
| extern const struct net_device_ops woal_netdev_ops; |
| #endif |
| #endif |
| extern int cfg80211_wext; |
| |
| extern int hw_test; |
| /******************************************************** |
| Local Functions |
| ********************************************************/ |
| /** |
| * @brief Parse a string to extract numerical arguments |
| * |
| * @param pos Pointer to the arguments string |
| * @param data Pointer to the arguments buffer |
| * @param datalen Length of the arguments buffer |
| * @param user_data_len Pointer to the number of arguments extracted |
| * |
| * @return MLAN_STATUS_SUCCESS |
| */ |
| mlan_status |
| parse_arguments(t_u8 *pos, int *data, int datalen, 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' || pos[i] == ' ') { |
| if (j >= datalen) { |
| j++; |
| break; |
| } |
| if (is_hex) { |
| data[j] = woal_atox(cdata); |
| is_hex = 0; |
| } else { |
| woal_atoi(&data[j], cdata); |
| } |
| j++; |
| k = 0; |
| memset(cdata, 0, sizeof(cdata)); |
| if (pos[i] == '\0') |
| break; |
| } else { |
| cdata[k] = pos[i]; |
| k++; |
| } |
| } |
| |
| *user_data_len = j; |
| 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) { |
| 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') { |
| /* set the beacon wps/p2p ies */ |
| pos += 2; |
| if (MLAN_STATUS_SUCCESS != |
| woal_cfg80211_mgmt_frame_ie(priv, pos, ie_len, NULL, 0, |
| NULL, 0, NULL, 0, |
| MGMT_MASK_BEACON_WPS_P2P, |
| MOAL_IOCTL_WAIT)) { |
| PRINTM(MERROR, "Failed to set beacon wps/p2p ie\n"); |
| ret = -EFAULT; |
| 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, |
| MOAL_IOCTL_WAIT)) { |
| 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, |
| MOAL_IOCTL_WAIT)) { |
| PRINTM(MERROR, "Failed to set assoc resp ie\n"); |
| ret = -EFAULT; |
| goto done; |
| } |
| } |
| |
| done: |
| LEAVE(); |
| return ret; |
| } |
| #endif |
| |
| #ifdef WIFI_DIRECT_SUPPORT |
| #if defined(STA_CFG80211) || defined(UAP_CFG80211) |
| /** |
| * @brief Set miracast mode |
| * |
| * @param priv Pointer to priv stucture |
| * @param pdata Pointer to cmd buffer |
| * @param len Length of data |
| * |
| * @return MLAN_STATUS_SUCCESS -- success, otherwise fail |
| */ |
| mlan_status |
| woal_set_miracast_mode(moal_private *priv, t_u8 *pdata, size_t len) |
| { |
| mlan_status ret = MLAN_STATUS_SUCCESS; |
| t_u8 *pos = pdata; |
| |
| ENTER(); |
| if (!pos) { |
| PRINTM(MERROR, "%s: Null buf!\n", __func__); |
| ret = MLAN_STATUS_FAILURE; |
| goto done; |
| } |
| while (!isdigit(*pos) && --len > 0) |
| pos++; |
| switch (*pos) { |
| case '0': |
| /* disable miracast mode */ |
| priv->phandle->miracast_mode = 0; |
| break; |
| case '1': |
| /* Source */ |
| priv->phandle->miracast_mode = 1; |
| break; |
| case '2': |
| /* Sink */ |
| priv->phandle->miracast_mode = 2; |
| break; |
| default: |
| PRINTM(MERROR, "%s: Unknown miracast mode (%c)\n", |
| priv->netdev->name, *pos); |
| ret = MLAN_STATUS_FAILURE; |
| break; |
| } |
| done: |
| LEAVE(); |
| return ret; |
| } |
| #endif |
| #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; |
| } |
| |
| /** |
| * @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; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| |
| 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); |
| |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| goto error; |
| } |
| memcpy(data_ptr + sizeof(buf_len), misc_cfg->param.hostcmd.cmd, |
| misc_cfg->param.hostcmd.len); |
| ret = misc_cfg->param.hostcmd.len + sizeof(buf_len) + |
| strlen(CMD_MARVELL) + strlen(PRIV_CMD_HOSTCMD); |
| memcpy(data_ptr, (t_u8 *)&ret, sizeof(t_u32)); |
| |
| error: |
| if (status != MLAN_STATUS_PENDING) |
| kfree(req); |
| |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief Custom IE 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_priv_customie(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| int ret = 0; |
| t_u8 *data_ptr; |
| mlan_ioctl_req *ioctl_req = NULL; |
| mlan_ds_misc_cfg *misc = NULL; |
| mlan_ds_misc_custom_ie *custom_ie = NULL; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| data_ptr = respbuf + (strlen(CMD_MARVELL) + strlen(PRIV_CMD_CUSTOMIE)); |
| |
| custom_ie = (mlan_ds_misc_custom_ie *)data_ptr; |
| 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; |
| misc->sub_command = MLAN_OID_MISC_CUSTOM_IE; |
| ioctl_req->req_id = MLAN_IOCTL_MISC_CFG; |
| if ((custom_ie->len == 0)||(custom_ie->len == |
| sizeof(custom_ie->ie_data_list[0]. |
| ie_index))) |
| ioctl_req->action = MLAN_ACT_GET; |
| else |
| ioctl_req->action = MLAN_ACT_SET; |
| |
| memcpy(&misc->param.cust_ie, custom_ie, sizeof(mlan_ds_misc_custom_ie)); |
| |
| status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| goto done; |
| } |
| custom_ie = (mlan_ds_misc_custom_ie *)data_ptr; |
| memcpy(custom_ie, &misc->param.cust_ie, sizeof(mlan_ds_misc_custom_ie)); |
| ret = sizeof(mlan_ds_misc_custom_ie); |
| if (ioctl_req->status_code == MLAN_ERROR_IOCTL_FAIL) { |
| /* send a separate error code to indicate error from driver */ |
| ret = EFAULT; |
| } |
| done: |
| if (status != MLAN_STATUS_PENDING) |
| kfree(ioctl_req); |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @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; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| |
| 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, |
| ARRAY_SIZE(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; |
| } |
| |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| 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 (status != MLAN_STATUS_PENDING) |
| 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) |
| { |
| t_u32 data[2]; |
| mlan_ioctl_req *req = NULL; |
| mlan_ds_11n_cfg *cfg_11n = NULL; |
| int ret = 0; |
| int user_data_len = 0; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| |
| 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, |
| ARRAY_SIZE(data), &user_data_len); |
| } |
| |
| if (user_data_len > 2) { |
| PRINTM(MERROR, "Too many 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; |
| } |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| goto done; |
| } |
| data[0] = cfg_11n->param.tx_cfg.httxcap; |
| PRINTM(MINFO, "GET: httxcap:0x%x\n", data[0]); |
| |
| if (req->action == MLAN_ACT_GET) { |
| cfg_11n->param.tx_cfg.httxcap = 0; |
| cfg_11n->param.tx_cfg.misc_cfg = BAND_SELECT_A; |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| goto done; |
| } |
| data[1] = cfg_11n->param.tx_cfg.httxcap; |
| PRINTM(MINFO, "GET: httxcap for 5GHz:0x%x\n", data[1]); |
| } |
| |
| memcpy(respbuf, data, sizeof(data)); |
| ret = sizeof(data); |
| |
| done: |
| if (status != MLAN_STATUS_PENDING) |
| kfree(req); |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief Set/Get 11n capability information |
| * |
| * @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_htcapinfo(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| int data[2]; |
| mlan_ioctl_req *req = NULL; |
| mlan_ds_11n_cfg *cfg_11n = NULL; |
| woal_ht_cap_info *ht_cap = NULL; |
| int ret = 0; |
| int user_data_len = 0; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| |
| if (strlen(respbuf) == |
| (strlen(CMD_MARVELL) + strlen(PRIV_CMD_HTCAPINFO))) { |
| /* GET operation */ |
| user_data_len = 0; |
| } else { |
| /* SET operation */ |
| memset((char *)data, 0, sizeof(data)); |
| parse_arguments(respbuf + strlen(CMD_MARVELL) + |
| strlen(PRIV_CMD_HTCAPINFO), data, |
| ARRAY_SIZE(data), &user_data_len); |
| } |
| |
| if (user_data_len > 2) { |
| PRINTM(MERROR, "Too many arguments\n"); |
| ret = -EINVAL; |
| goto done; |
| } |
| req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); |
| if (req == NULL) { |
| ret = -ENOMEM; |
| goto done; |
| } |
| |
| cfg_11n = (mlan_ds_11n_cfg *)req->pbuf; |
| cfg_11n->sub_command = MLAN_OID_11N_HTCAP_CFG; |
| req->req_id = MLAN_IOCTL_11N_CFG; |
| |
| if (user_data_len == 0) { |
| /* Get 11n tx parameters from MLAN */ |
| req->action = MLAN_ACT_GET; |
| cfg_11n->param.htcap_cfg.misc_cfg = BAND_SELECT_BG; |
| } else { |
| 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 (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.htcap_cfg.misc_cfg = data[1]; |
| PRINTM(MINFO, "SET: htcapinfo band:0x%x\n", data[1]); |
| } |
| /* Update 11n tx parameters in MLAN */ |
| req->action = MLAN_ACT_SET; |
| } |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| goto done; |
| } |
| data[0] = cfg_11n->param.htcap_cfg.htcap; |
| PRINTM(MINFO, "GET: htcapinfo for 2.4GHz:0x%x\n", data[0]); |
| |
| if (req->action == MLAN_ACT_GET) { |
| cfg_11n->param.htcap_cfg.htcap = 0; |
| cfg_11n->param.htcap_cfg.misc_cfg = BAND_SELECT_A; |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| goto done; |
| } |
| data[1] = cfg_11n->param.htcap_cfg.htcap; |
| PRINTM(MINFO, "GET: htcapinfo for 5GHz:0x%x\n", data[1]); |
| } |
| |
| ht_cap = (woal_ht_cap_info *)respbuf; |
| ht_cap->ht_cap_info_bg = data[0]; |
| ht_cap->ht_cap_info_a = data[1]; |
| ret = sizeof(woal_ht_cap_info); |
| |
| done: |
| if (status != MLAN_STATUS_PENDING) |
| kfree(req); |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief Set/Get add BA parameters |
| * |
| * @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_addbapara(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| int data[5]; |
| mlan_ioctl_req *req = NULL; |
| mlan_ds_11n_cfg *cfg_11n = NULL; |
| woal_addba *addba = NULL; |
| int ret = 0; |
| int user_data_len = 0; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| |
| if (strlen(respbuf) == |
| (strlen(CMD_MARVELL) + strlen(PRIV_CMD_ADDBAPARA))) { |
| /* GET operation */ |
| user_data_len = 0; |
| } else { |
| /* SET operation */ |
| memset((char *)data, 0, sizeof(data)); |
| parse_arguments(respbuf + strlen(CMD_MARVELL) + |
| strlen(PRIV_CMD_ADDBAPARA), data, |
| ARRAY_SIZE(data), &user_data_len); |
| |
| if (user_data_len != ARRAY_SIZE(data)) { |
| PRINTM(MERROR, "Invalid number of arguments\n"); |
| ret = -EINVAL; |
| goto done; |
| } |
| if (data[0] < 0 || data[0] > MLAN_DEFAULT_BLOCK_ACK_TIMEOUT) { |
| PRINTM(MERROR, "Incorrect addba timeout value.\n"); |
| ret = -EFAULT; |
| goto done; |
| } |
| 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 done; |
| } |
| if (data[3] < 0 || data[3] > 1 || data[4] < 0 || data[4] > 1) { |
| PRINTM(MERROR, "Incorrect Tx/Rx amsdu.\n"); |
| ret = -EFAULT; |
| 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_ADDBA_PARAM; |
| req->req_id = MLAN_IOCTL_11N_CFG; |
| |
| if (user_data_len == 0) { |
| /* Get add BA parameters from MLAN */ |
| req->action = MLAN_ACT_GET; |
| } else { |
| cfg_11n->param.addba_param.timeout = data[0]; |
| cfg_11n->param.addba_param.txwinsize = data[1]; |
| cfg_11n->param.addba_param.rxwinsize = data[2]; |
| cfg_11n->param.addba_param.txamsdu = data[3]; |
| cfg_11n->param.addba_param.rxamsdu = data[4]; |
| PRINTM(MINFO, |
| "SET: timeout:%d txwinsize:%d rxwinsize:%d txamsdu=%d rxamsdu=%d\n", |
| data[0], data[1], data[2], data[3], data[4]); |
| /* Update add BA parameters in MLAN */ |
| req->action = MLAN_ACT_SET; |
| } |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| goto done; |
| } |
| |
| addba = (woal_addba *)respbuf; |
| |
| addba->time_out = cfg_11n->param.addba_param.timeout; |
| addba->tx_win_size = cfg_11n->param.addba_param.txwinsize; |
| addba->rx_win_size = cfg_11n->param.addba_param.rxwinsize; |
| addba->tx_amsdu = cfg_11n->param.addba_param.txamsdu; |
| addba->rx_amsdu = cfg_11n->param.addba_param.rxamsdu; |
| PRINTM(MINFO, |
| "GET: timeout:%d txwinsize:%d rxwinsize:%d txamsdu=%d, rxamsdu=%d\n", |
| addba->time_out, addba->tx_win_size, addba->rx_win_size, |
| addba->tx_amsdu, addba->rx_amsdu); |
| |
| ret = sizeof(woal_addba); |
| |
| done: |
| if (status != MLAN_STATUS_PENDING) |
| kfree(req); |
| LEAVE(); |
| return ret; |
| |
| } |
| |
| /** |
| * @brief Delete selective BA based on parameters |
| * |
| * @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_delba(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| t_u32 data[2] = { 0xFF, 0xFF }; |
| mlan_ioctl_req *req = NULL; |
| mlan_ds_11n_cfg *cfg_11n = NULL; |
| mlan_ds_11n_delba *del_ba = NULL; |
| int ret = 0; |
| int user_data_len = 0; |
| int header_len = 0; |
| t_u8 *mac_pos = NULL; |
| t_u8 peer_mac[ETH_ALEN] = { 0 }; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| |
| header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_DELBA); |
| |
| if (strlen(respbuf) == header_len) { |
| /* Incorrect number of arguments */ |
| PRINTM(MERROR, "%d: Invalid arguments\n", __LINE__); |
| ret = -EINVAL; |
| goto done; |
| } |
| |
| mac_pos = strstr(respbuf + header_len, " "); |
| if (mac_pos) |
| mac_pos = strstr(mac_pos + 1, " "); |
| if (mac_pos) { |
| #define MAC_STRING_LENGTH 17 |
| if (strlen(mac_pos + 1) != MAC_STRING_LENGTH) { |
| PRINTM(MERROR, "%d: Invalid arguments\n", __LINE__); |
| ret = -EINVAL; |
| goto done; |
| } |
| woal_mac2u8(peer_mac, mac_pos + 1); |
| *mac_pos = '\0'; |
| } |
| |
| parse_arguments(respbuf + header_len, data, ARRAY_SIZE(data), |
| &user_data_len); |
| |
| if (mac_pos) |
| user_data_len++; |
| |
| if (user_data_len > 3 || |
| (!(data[0] & (DELBA_TX | DELBA_RX))) || |
| (data[1] != DELBA_ALL_TIDS && !(data[1] <= 7))) { |
| /* Incorrect number of arguments */ |
| PRINTM(MERROR, "%d: Invalid arguments\n", __LINE__); |
| 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; |
| req->req_id = MLAN_IOCTL_11N_CFG; |
| cfg_11n->sub_command = MLAN_OID_11N_CFG_DELBA; |
| |
| del_ba = &cfg_11n->param.del_ba; |
| memset(del_ba, 0, sizeof(mlan_ds_11n_delba)); |
| del_ba->direction = (t_u8)data[0]; |
| del_ba->tid = DELBA_ALL_TIDS; |
| if (user_data_len > 1) |
| del_ba->tid = (t_u8)data[1]; |
| if (user_data_len > 2) |
| memcpy(del_ba->peer_mac_addr, peer_mac, ETH_ALEN); |
| |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| goto done; |
| } |
| |
| ret = sprintf(respbuf, "OK. BA deleted successfully.\n") + 1; |
| |
| done: |
| if (status != MLAN_STATUS_PENDING) |
| kfree(req); |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief Set/Get the reject addba requst conditions |
| * |
| * @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_rejectaddbareq(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| t_u32 data[1]; |
| mlan_ioctl_req *req = NULL; |
| mlan_ds_11n_cfg *cfg_11n = NULL; |
| int ret = 0; |
| int user_data_len = 0; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| |
| if (strlen(respbuf) == |
| (strlen(CMD_MARVELL) + strlen(PRIV_CMD_REJECTADDBAREQ))) { |
| /* GET operation */ |
| user_data_len = 0; |
| } else { |
| /* SET operation */ |
| memset((char *)data, 0, sizeof(data)); |
| parse_arguments(respbuf + strlen(CMD_MARVELL) + |
| strlen(PRIV_CMD_REJECTADDBAREQ), data, |
| ARRAY_SIZE(data), &user_data_len); |
| } |
| |
| if (user_data_len > 1) { |
| PRINTM(MERROR, "Too many 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_REJECT_ADDBA_REQ; |
| req->req_id = MLAN_IOCTL_11N_CFG; |
| |
| if (user_data_len == 0) { |
| /* Get the reject addba req conditions */ |
| req->action = MLAN_ACT_GET; |
| } else { |
| /* Set the reject addba req conditions */ |
| cfg_11n->param.reject_addba_req.conditions = data[0]; |
| req->action = MLAN_ACT_SET; |
| } |
| |
| /* Send IOCTL request to MLAN */ |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| goto done; |
| } |
| if (req->action == MLAN_ACT_GET) { |
| sprintf(respbuf, "0x%x", |
| cfg_11n->param.reject_addba_req.conditions); |
| ret = strlen(respbuf) + 1; |
| } else { |
| ret = sprintf(respbuf, "OK\n") + 1; |
| } |
| |
| done: |
| if (status != MLAN_STATUS_PENDING) |
| kfree(req); |
| LEAVE(); |
| return ret; |
| |
| } |
| |
| /** |
| * @brief Set/Get the addba reject setting |
| * |
| * @param priv A pointer to moal_private structure |
| * @param action Action set or get |
| * @param addba_reject A pointer to addba_reject array. |
| * |
| * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail |
| */ |
| mlan_status |
| woal_ioctl_addba_reject(moal_private *priv, t_u32 action, t_u8 *addba_reject) |
| { |
| mlan_ioctl_req *req = NULL; |
| mlan_ds_11n_cfg *cfg_11n = NULL; |
| mlan_status ret = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); |
| if (req == NULL) { |
| ret = -ENOMEM; |
| goto done; |
| } |
| |
| cfg_11n = (mlan_ds_11n_cfg *)req->pbuf; |
| cfg_11n->sub_command = MLAN_OID_11N_CFG_ADDBA_REJECT; |
| req->req_id = MLAN_IOCTL_11N_CFG; |
| |
| req->action = action; |
| if (action == MLAN_ACT_SET) |
| memcpy(cfg_11n->param.addba_reject, addba_reject, |
| sizeof(cfg_11n->param.addba_reject)); |
| /* Send IOCTL request to MLAN */ |
| ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (ret != MLAN_STATUS_SUCCESS) |
| goto done; |
| if (action == MLAN_ACT_GET) |
| memcpy(addba_reject, cfg_11n->param.addba_reject, |
| sizeof(cfg_11n->param.addba_reject)); |
| done: |
| if (ret != MLAN_STATUS_PENDING) |
| kfree(req); |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief Set/Get addba prio_tbl |
| * |
| * @param priv A pointer to moal_private structure |
| * @param action Action set or get |
| * @param aggr_prio_tbl A pointer to mlan_ds_11n_aggr_prio_tbl. |
| * |
| * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail |
| */ |
| mlan_status |
| woal_ioctl_aggr_prio_tbl(moal_private *priv, t_u32 action, |
| mlan_ds_11n_aggr_prio_tbl *aggr_prio_tbl) |
| { |
| mlan_ioctl_req *req = NULL; |
| mlan_ds_11n_cfg *cfg_11n = NULL; |
| mlan_status ret = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| |
| req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); |
| if (req == NULL) { |
| ret = -ENOMEM; |
| goto done; |
| } |
| |
| cfg_11n = (mlan_ds_11n_cfg *)req->pbuf; |
| cfg_11n->sub_command = MLAN_OID_11N_CFG_AGGR_PRIO_TBL; |
| req->req_id = MLAN_IOCTL_11N_CFG; |
| |
| req->action = action; |
| if (action == MLAN_ACT_SET) |
| memcpy(&cfg_11n->param.aggr_prio_tbl, aggr_prio_tbl, |
| sizeof(mlan_ds_11n_aggr_prio_tbl)); |
| /* Send IOCTL request to MLAN */ |
| ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (ret != MLAN_STATUS_SUCCESS) |
| goto done; |
| if (action == MLAN_ACT_GET) |
| memcpy(aggr_prio_tbl, &cfg_11n->param.aggr_prio_tbl, |
| sizeof(mlan_ds_11n_aggr_prio_tbl)); |
| done: |
| if (ret != MLAN_STATUS_PENDING) |
| kfree(req); |
| LEAVE(); |
| return ret; |
| |
| } |
| |
| /** |
| * @brief Set/Get addba_param |
| * |
| * @param priv A pointer to moal_private structure |
| * @param action Action set or get |
| * @param addba_param A pointer to mlan_ds_11n_addba_param. |
| * |
| * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail |
| */ |
| mlan_status |
| woal_ioctl_addba_param(moal_private *priv, t_u32 action, |
| mlan_ds_11n_addba_param *addba_param) |
| { |
| mlan_ioctl_req *req = NULL; |
| mlan_ds_11n_cfg *cfg_11n = NULL; |
| mlan_status ret = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| |
| req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); |
| if (req == NULL) { |
| ret = -ENOMEM; |
| goto done; |
| } |
| |
| cfg_11n = (mlan_ds_11n_cfg *)req->pbuf; |
| cfg_11n->sub_command = MLAN_OID_11N_CFG_ADDBA_PARAM; |
| req->req_id = MLAN_IOCTL_11N_CFG; |
| |
| req->action = action; |
| if (action == MLAN_ACT_SET) |
| memcpy(&cfg_11n->param.addba_param, addba_param, |
| sizeof(mlan_ds_11n_addba_param)); |
| /* Send IOCTL request to MLAN */ |
| ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (ret != MLAN_STATUS_SUCCESS) |
| goto done; |
| if (action == MLAN_ACT_GET) |
| memcpy(addba_param, &cfg_11n->param.addba_param, |
| sizeof(mlan_ds_11n_addba_param)); |
| done: |
| if (ret != MLAN_STATUS_PENDING) |
| kfree(req); |
| LEAVE(); |
| return ret; |
| |
| } |
| |
| /** |
| * @brief Configuring rx block-ack window size |
| * |
| * @param priv A pointer to moal_private structure |
| * @param respbuf A pointer to response buffer |
| * @param respbuflen Available length of response buffer |
| * |
| * @return 0 --success, otherwise failure |
| */ |
| int |
| woal_set_rx_ba_winsize(moal_private *priv, t_u8 *respbuf, int respbuflen) |
| { |
| int data[2]; |
| t_u8 addba_reject[MAX_NUM_TID]; |
| mlan_ds_11n_addba_param addba_param; |
| int ret = 0; |
| int user_data_len = 0; |
| |
| ENTER(); |
| |
| memset((char *)data, 0, sizeof(data)); |
| if (respbuf && strlen(respbuf) > 0) |
| parse_arguments(respbuf, data, ARRAY_SIZE(data), |
| &user_data_len); |
| |
| if (user_data_len != 2) { |
| PRINTM(MERROR, "Invalid arguments for ba_winsize command\n"); |
| ret = -EINVAL; |
| goto done; |
| } |
| if (data[0] > 7 || data[0] < 0) { |
| PRINTM(MERROR, "Invalid tid %d\n", data[0]); |
| ret = -EINVAL; |
| goto done; |
| } |
| if (data[1] < 0) { |
| PRINTM(MERROR, "Invalid winsize %d\n", data[1]); |
| ret = -EINVAL; |
| goto done; |
| } |
| memset(addba_reject, 0, sizeof(addba_reject)); |
| if (MLAN_STATUS_SUCCESS != |
| woal_ioctl_addba_reject(priv, MLAN_ACT_GET, addba_reject)) { |
| ret = -EFAULT; |
| goto done; |
| } |
| /* disable tx ba */ |
| if (data[1] == 0) { |
| addba_reject[data[0]] = MTRUE; |
| if (MLAN_STATUS_SUCCESS != |
| woal_ioctl_addba_reject(priv, MLAN_ACT_SET, addba_reject)) |
| ret = -EFAULT; |
| } else { |
| if (addba_reject[data[0]] == MTRUE) { |
| addba_reject[data[0]] = MFALSE; |
| if (MLAN_STATUS_SUCCESS != |
| woal_ioctl_addba_reject(priv, MLAN_ACT_SET, |
| addba_reject)) { |
| ret = -EFAULT; |
| goto done; |
| } |
| } |
| memset(&addba_param, 0, sizeof(addba_param)); |
| if (MLAN_STATUS_SUCCESS != |
| woal_ioctl_addba_param(priv, MLAN_ACT_GET, &addba_param)) { |
| ret = -EFAULT; |
| goto done; |
| } |
| if (data[1] != addba_param.rxwinsize) { |
| addba_param.rxwinsize = data[1]; |
| if (MLAN_STATUS_SUCCESS != |
| woal_ioctl_addba_param(priv, MLAN_ACT_SET, |
| &addba_param)) |
| ret = -EFAULT; |
| } |
| |
| } |
| done: |
| LEAVE(); |
| return ret; |
| |
| } |
| |
| /** |
| * @brief Configuring trx block-ack window size |
| * |
| * @param priv A pointer to moal_private structure |
| * @param respbuf A pointer to response buffer |
| * @param respbuflen Available length of response buffer |
| * |
| * @return 0 --success, otherwise failure |
| */ |
| int |
| woal_set_tx_ba_winsize(moal_private *priv, t_u8 *respbuf, int respbuflen) |
| { |
| int data[2]; |
| mlan_ds_11n_aggr_prio_tbl aggr_prio_tbl; |
| mlan_ds_11n_addba_param addba_param; |
| t_u8 tos_to_tid_inv[] = { |
| 0x02, 0x00, 0x01, 0x03, |
| 0x04, 0x05, 0x06, 0x07 |
| }; |
| int ret = 0; |
| int user_data_len = 0; |
| |
| ENTER(); |
| |
| memset((char *)data, 0, sizeof(data)); |
| if (respbuf && strlen(respbuf) > 0) |
| parse_arguments(respbuf, data, ARRAY_SIZE(data), |
| &user_data_len); |
| |
| if (user_data_len != 2) { |
| PRINTM(MERROR, "Invalid arguments for ba_winsize command\n"); |
| ret = -EINVAL; |
| goto done; |
| } |
| if (data[0] > 7 || data[0] < 0) { |
| PRINTM(MERROR, "Invalid tid %d\n", data[0]); |
| ret = -EINVAL; |
| goto done; |
| } |
| if (data[1] < 0) { |
| PRINTM(MERROR, "Invalid winsize %d\n", data[1]); |
| ret = -EINVAL; |
| goto done; |
| } |
| memset(&aggr_prio_tbl, 0, sizeof(aggr_prio_tbl)); |
| if (MLAN_STATUS_SUCCESS != |
| woal_ioctl_aggr_prio_tbl(priv, MLAN_ACT_GET, &aggr_prio_tbl)) { |
| ret = -EFAULT; |
| goto done; |
| } |
| /* disable tx ba */ |
| if (data[1] == 0) { |
| if (aggr_prio_tbl.ampdu[data[0]] != 0xff) { |
| aggr_prio_tbl.ampdu[data[0]] = 0xff; |
| if (MLAN_STATUS_SUCCESS != |
| woal_ioctl_aggr_prio_tbl(priv, MLAN_ACT_SET, |
| &aggr_prio_tbl)) |
| ret = -EFAULT; |
| } |
| } else { |
| if (aggr_prio_tbl.ampdu[data[0]] == 0xff) { |
| aggr_prio_tbl.ampdu[data[0]] = tos_to_tid_inv[data[0]]; |
| if (MLAN_STATUS_SUCCESS != |
| woal_ioctl_aggr_prio_tbl(priv, MLAN_ACT_SET, |
| &aggr_prio_tbl)) { |
| ret = -EFAULT; |
| goto done; |
| } |
| } |
| memset(&addba_param, 0, sizeof(addba_param)); |
| if (MLAN_STATUS_SUCCESS != |
| woal_ioctl_addba_param(priv, MLAN_ACT_GET, &addba_param)) { |
| ret = -EFAULT; |
| goto done; |
| } |
| if (data[1] != addba_param.txwinsize) { |
| addba_param.txwinsize = data[1]; |
| if (MLAN_STATUS_SUCCESS != |
| woal_ioctl_addba_param(priv, MLAN_ACT_SET, |
| &addba_param)) |
| ret = -EFAULT; |
| } |
| |
| } |
| done: |
| LEAVE(); |
| return ret; |
| |
| } |
| |
| /** |
| * @brief Set/Get aggregation priority table 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_aggrpriotbl(moal_private *priv, t_u8 *respbuf, |
| t_u32 respbuflen) |
| { |
| int data[MAX_NUM_TID * 2], i, j; |
| mlan_ioctl_req *req = NULL; |
| mlan_ds_11n_cfg *cfg_11n = NULL; |
| int ret = 0; |
| int user_data_len = 0; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| |
| if (strlen(respbuf) == |
| (strlen(CMD_MARVELL) + strlen(PRIV_CMD_AGGRPRIOTBL))) { |
| /* GET operation */ |
| user_data_len = 0; |
| } else { |
| /* SET operation */ |
| memset((char *)data, 0, sizeof(data)); |
| parse_arguments(respbuf + strlen(CMD_MARVELL) + |
| strlen(PRIV_CMD_AGGRPRIOTBL), data, |
| ARRAY_SIZE(data), &user_data_len); |
| |
| if (user_data_len != ARRAY_SIZE(data)) { |
| PRINTM(MERROR, "Invalid number of arguments\n"); |
| ret = -EINVAL; |
| goto done; |
| } |
| for (i = 0, j = 0; i < user_data_len; 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 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_AGGR_PRIO_TBL; |
| req->req_id = MLAN_IOCTL_11N_CFG; |
| |
| if (user_data_len == 0) { |
| /* Get aggr priority table from MLAN */ |
| req->action = MLAN_ACT_GET; |
| } else { |
| for (i = 0, j = 0; i < user_data_len; i = i + 2, ++j) { |
| cfg_11n->param.aggr_prio_tbl.ampdu[j] = data[i]; |
| cfg_11n->param.aggr_prio_tbl.amsdu[j] = data[i + 1]; |
| } |
| /* Update aggr priority table in MLAN */ |
| req->action = MLAN_ACT_SET; |
| } |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| goto done; |
| } |
| |
| for (i = 0, j = 0; i < (MAX_NUM_TID * 2); i = i + 2, ++j) { |
| respbuf[i] = cfg_11n->param.aggr_prio_tbl.ampdu[j]; |
| respbuf[i + 1] = cfg_11n->param.aggr_prio_tbl.amsdu[j]; |
| } |
| |
| ret = sizeof(data); |
| |
| done: |
| if (status != MLAN_STATUS_PENDING) |
| kfree(req); |
| LEAVE(); |
| return ret; |
| |
| } |
| |
| /** |
| * @brief Set/Get Add BA reject 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_addbareject(moal_private *priv, t_u8 *respbuf, |
| t_u32 respbuflen) |
| { |
| int data[MAX_NUM_TID], i; |
| mlan_ioctl_req *req = NULL; |
| mlan_ds_11n_cfg *cfg_11n = NULL; |
| int ret = 0; |
| int user_data_len = 0; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| |
| if (strlen(respbuf) == |
| (strlen(CMD_MARVELL) + strlen(PRIV_CMD_ADDBAREJECT))) { |
| /* GET operation */ |
| user_data_len = 0; |
| } else { |
| /* SET operation */ |
| memset((char *)data, 0, sizeof(data)); |
| parse_arguments(respbuf + strlen(CMD_MARVELL) + |
| strlen(PRIV_CMD_ADDBAREJECT), data, |
| ARRAY_SIZE(data), &user_data_len); |
| |
| if (user_data_len != ARRAY_SIZE(data)) { |
| PRINTM(MERROR, "Invalid number of arguments\n"); |
| ret = -EINVAL; |
| goto done; |
| } |
| for (i = 0; i < user_data_len; i++) { |
| if (data[i] != 0 && data[i] != 1) { |
| PRINTM(MERROR, |
| "addba reject only takes argument as 0 or 1\n"); |
| ret = -EFAULT; |
| 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_ADDBA_REJECT; |
| req->req_id = MLAN_IOCTL_11N_CFG; |
| |
| if (user_data_len == 0) { |
| /* Get add BA reject configuration from MLAN */ |
| req->action = MLAN_ACT_GET; |
| } else { |
| for (i = 0; i < user_data_len; i++) |
| cfg_11n->param.addba_reject[i] = data[i]; |
| /* Update add BA reject configuration in MLAN */ |
| req->action = MLAN_ACT_SET; |
| } |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| goto done; |
| } |
| |
| for (i = 0; i < MAX_NUM_TID; i++) |
| respbuf[i] = cfg_11n->param.addba_reject[i]; |
| |
| ret = sizeof(data); |
| |
| done: |
| if (status != MLAN_STATUS_PENDING) |
| 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; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| |
| req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_rate)); |
| if (req == NULL) { |
| ret = -ENOMEM; |
| goto done; |
| } |
| |
| rate = (mlan_ds_rate *)req->pbuf; |
| rate->sub_command = MLAN_OID_GET_DATA_RATE; |
| req->req_id = MLAN_IOCTL_RATE; |
| req->action = MLAN_ACT_GET; |
| |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| goto done; |
| } |
| |
| data_rate = (mlan_data_rate *)respbuf; |
| |
| memcpy(data_rate, &rate->param.data_rate, sizeof(mlan_data_rate)); |
| |
| ret = sizeof(mlan_data_rate); |
| |
| done: |
| if (status != MLAN_STATUS_PENDING) |
| 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; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| |
| 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, |
| ARRAY_SIZE(data), &user_data_len); |
| } |
| |
| if (user_data_len >= 4) { |
| PRINTM(MERROR, "Too many 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] > 7)) |
| ) { |
| 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; |
| } |
| |
| } |
| |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| 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 (status != MLAN_STATUS_PENDING) |
| kfree(req); |
| LEAVE(); |
| return ret; |
| |
| } |
| |
| #ifdef STA_SUPPORT |
| /** |
| * @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)); |
| #if defined(STA_WEXT) || defined(UAP_WEXT) |
| 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; |
| #endif |
| } |
| done: |
| if (status != MLAN_STATUS_PENDING) |
| kfree(req); |
| LEAVE(); |
| return status; |
| } |
| |
| /** |
| * @brief Get wireless stats information |
| * |
| * @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_getlog(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| int ret = 0; |
| mlan_ds_get_stats *stats; |
| ENTER(); |
| |
| if (respbuflen < sizeof(*stats)) { |
| PRINTM(MERROR, "Get log: respbuflen (%d) too small!", |
| (int)respbuflen); |
| ret = -EFAULT; |
| goto done; |
| } |
| stats = (mlan_ds_get_stats *)respbuf; |
| if (MLAN_STATUS_SUCCESS != |
| woal_get_stats_info(priv, MOAL_IOCTL_WAIT, stats)) { |
| PRINTM(MERROR, "Get log: Failed to get stats info!"); |
| ret = -EFAULT; |
| goto done; |
| } |
| |
| ret = sizeof(mlan_ds_get_stats); |
| |
| done: |
| LEAVE(); |
| return ret; |
| } |
| #endif |
| |
| /** |
| * @brief Set/Get esupplicant mode 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_esuppmode(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| t_u32 data[3]; |
| mlan_ioctl_req *req = NULL; |
| mlan_ds_sec_cfg *sec = NULL; |
| woal_esuppmode_cfg *esupp_mode = NULL; |
| int ret = 0; |
| int user_data_len = 0; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| |
| if (!priv->phandle->card_info->embedded_supp) { |
| PRINTM(MERROR, "Not supported cmd on this card\n"); |
| ret = -EOPNOTSUPP; |
| goto done; |
| } |
| if (strlen(respbuf) == |
| (strlen(CMD_MARVELL) + strlen(PRIV_CMD_ESUPPMODE))) { |
| /* GET operation */ |
| user_data_len = 0; |
| } else { |
| /* SET operation */ |
| memset((char *)data, 0, sizeof(data)); |
| parse_arguments(respbuf + strlen(CMD_MARVELL) + |
| strlen(PRIV_CMD_ESUPPMODE), data, |
| ARRAY_SIZE(data), &user_data_len); |
| } |
| |
| if (user_data_len >= 4 || user_data_len == 1 || user_data_len == 2) { |
| PRINTM(MERROR, "Invalid number of arguments\n"); |
| ret = -EINVAL; |
| goto done; |
| } |
| |
| req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg)); |
| if (req == NULL) { |
| ret = -ENOMEM; |
| goto done; |
| } |
| |
| req->req_id = MLAN_IOCTL_SEC_CFG; |
| sec = (mlan_ds_sec_cfg *)req->pbuf; |
| sec->sub_command = MLAN_OID_SEC_CFG_ESUPP_MODE; |
| |
| if (user_data_len == 0) { |
| /* Get operation */ |
| req->action = MLAN_ACT_GET; |
| } else { |
| /* Set operation */ |
| req->action = MLAN_ACT_SET; |
| /* RSN mode */ |
| sec->param.esupp_mode.rsn_mode = data[0]; |
| /* Pairwise cipher */ |
| sec->param.esupp_mode.act_paircipher = (data[1] & 0xFF); |
| /* Group cipher */ |
| sec->param.esupp_mode.act_groupcipher = (data[2] & 0xFF); |
| } |
| |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| goto done; |
| } |
| |
| esupp_mode = (woal_esuppmode_cfg *)respbuf; |
| esupp_mode->rsn_mode = |
| (t_u16)((sec->param.esupp_mode.rsn_mode) & 0xFFFF); |
| esupp_mode->pairwise_cipher = |
| (t_u8)((sec->param.esupp_mode.act_paircipher) & 0xFF); |
| esupp_mode->group_cipher = |
| (t_u8)((sec->param.esupp_mode.act_groupcipher) & 0xFF); |
| |
| ret = sizeof(woal_esuppmode_cfg); |
| done: |
| if (status != MLAN_STATUS_PENDING) |
| kfree(req); |
| LEAVE(); |
| return ret; |
| |
| } |
| |
| /** |
| * @brief Set/Get esupplicant passphrase 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_passphrase(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| mlan_ioctl_req *req = NULL; |
| mlan_ds_sec_cfg *sec = NULL; |
| int ret = 0, action = -1, i = 0; |
| char *begin, *end, *opt; |
| t_u16 len = 0; |
| t_u8 zero_mac[] = { 0, 0, 0, 0, 0, 0 }; |
| t_u8 *mac = NULL; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| if (!priv->phandle->card_info->embedded_supp) { |
| PRINTM(MERROR, "Not supported cmd on this card\n"); |
| ret = -EOPNOTSUPP; |
| goto done; |
| } |
| |
| if (strlen(respbuf) == |
| (strlen(CMD_MARVELL) + strlen(PRIV_CMD_PASSPHRASE))) { |
| PRINTM(MERROR, "No arguments provided\n"); |
| ret = -EINVAL; |
| goto done; |
| } |
| |
| /* Parse the buf to get the cmd_action */ |
| begin = respbuf + strlen(CMD_MARVELL) + strlen(PRIV_CMD_PASSPHRASE); |
| 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; |
| } |
| |
| req->req_id = MLAN_IOCTL_SEC_CFG; |
| sec = (mlan_ds_sec_cfg *)req->pbuf; |
| sec->sub_command = MLAN_OID_SEC_CFG_PASSPHRASE; |
| 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; |
| memcpy(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; |
| else if (action == 0) |
| sec->param.passphrase.psk_type = MLAN_PSK_QUERY; |
| |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| goto done; |
| } |
| |
| memset(respbuf, 0, respbuflen); |
| if (sec->param.passphrase.ssid.ssid_len) { |
| len += sprintf(respbuf + len, "ssid:"); |
| memcpy(respbuf + len, sec->param.passphrase.ssid.ssid, |
| sec->param.passphrase.ssid.ssid_len); |
| len += sec->param.passphrase.ssid.ssid_len; |
| len += sprintf(respbuf + len, " "); |
| } |
| if (memcmp(&sec->param.passphrase.bssid, zero_mac, sizeof(zero_mac))) { |
| mac = (t_u8 *)&sec->param.passphrase.bssid; |
| len += sprintf(respbuf + len, "bssid:"); |
| for (i = 0; i < ETH_ALEN - 1; ++i) |
| len += sprintf(respbuf + len, "%02x:", mac[i]); |
| len += sprintf(respbuf + len, "%02x ", mac[i]); |
| } |
| if (sec->param.passphrase.psk_type == MLAN_PSK_PMK) { |
| len += sprintf(respbuf + len, "psk:"); |
| for (i = 0; i < MLAN_MAX_KEY_LENGTH; ++i) |
| len += sprintf(respbuf + len, "%02x", |
| sec->param.passphrase.psk.pmk.pmk[i]); |
| len += sprintf(respbuf + len, "\n"); |
| } |
| if (sec->param.passphrase.psk_type == MLAN_PSK_PASSPHRASE) |
| len += sprintf(respbuf + len, "passphrase:%s\n", |
| sec->param.passphrase.psk.passphrase.passphrase); |
| |
| ret = len; |
| done: |
| if (status != MLAN_STATUS_PENDING) |
| kfree(req); |
| LEAVE(); |
| return ret; |
| |
| } |
| |
| /** |
| * @brief Deauthenticate |
| * |
| * @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_deauth(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| int ret = 0; |
| t_u8 mac[ETH_ALEN]; |
| |
| ENTER(); |
| |
| if (strlen(respbuf) > (strlen(CMD_MARVELL) + strlen(PRIV_CMD_DEAUTH))) { |
| /* Deauth mentioned BSSID */ |
| woal_mac2u8(mac, |
| respbuf + strlen(CMD_MARVELL) + |
| strlen(PRIV_CMD_DEAUTH)); |
| if (MLAN_STATUS_SUCCESS != |
| woal_disconnect(priv, MOAL_IOCTL_WAIT, mac)) { |
| ret = -EFAULT; |
| goto done; |
| } |
| } else { |
| if (MLAN_STATUS_SUCCESS != |
| woal_disconnect(priv, MOAL_IOCTL_WAIT, NULL)) |
| ret = -EFAULT; |
| } |
| |
| done: |
| LEAVE(); |
| return ret; |
| } |
| |
| #ifdef UAP_SUPPORT |
| /** |
| * @brief uap station deauth ioctl handler |
| * |
| * @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. |
| */ |
| static int |
| woal_priv_ap_deauth(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| t_u8 *data_ptr; |
| mlan_ioctl_req *ioctl_req = NULL; |
| mlan_ds_bss *bss = NULL; |
| mlan_deauth_param deauth_param; |
| int ret = 0; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| |
| data_ptr = respbuf + (strlen(CMD_MARVELL) + strlen(PRIV_CMD_AP_DEAUTH)); |
| memset(&deauth_param, 0, sizeof(mlan_deauth_param)); |
| memcpy(&deauth_param, data_ptr, sizeof(mlan_deauth_param)); |
| |
| PRINTM(MIOCTL, "ioctl deauth station: " MACSTR ", reason=%d\n", |
| MAC2STR(deauth_param.mac_addr), deauth_param.reason_code); |
| |
| ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); |
| if (ioctl_req == NULL) { |
| ret = -ENOMEM; |
| goto done; |
| } |
| bss = (mlan_ds_bss *)ioctl_req->pbuf; |
| |
| bss->sub_command = MLAN_OID_UAP_DEAUTH_STA; |
| ioctl_req->req_id = MLAN_IOCTL_BSS; |
| ioctl_req->action = MLAN_ACT_SET; |
| |
| memcpy(&bss->param.deauth_param, &deauth_param, |
| sizeof(mlan_deauth_param)); |
| status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| goto done; |
| } |
| |
| memcpy(data_ptr, &ioctl_req->status_code, sizeof(t_u32)); |
| ret = sizeof(t_u32); |
| done: |
| if (status != MLAN_STATUS_PENDING) |
| kfree(ioctl_req); |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief uap get station list handler |
| * |
| * @param dev A pointer to net_device structure |
| * @param req A pointer to ifreq structure |
| * @return 0 --success, otherwise fail |
| */ |
| static int |
| woal_priv_get_sta_list(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| int ret = 0; |
| mlan_ds_get_info *info = NULL; |
| mlan_ds_sta_list *sta_list = NULL; |
| mlan_ioctl_req *ioctl_req = NULL; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| |
| /* Allocate an IOCTL request buffer */ |
| ioctl_req = |
| (mlan_ioctl_req *) |
| woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_get_info)); |
| if (ioctl_req == NULL) { |
| ret = -ENOMEM; |
| goto done; |
| } |
| |
| info = (mlan_ds_get_info *)ioctl_req->pbuf; |
| info->sub_command = MLAN_OID_UAP_STA_LIST; |
| ioctl_req->req_id = MLAN_IOCTL_GET_INFO; |
| ioctl_req->action = MLAN_ACT_GET; |
| |
| status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| goto done; |
| } |
| |
| sta_list = |
| (mlan_ds_sta_list *)(respbuf + strlen(CMD_MARVELL) + |
| strlen(PRIV_CMD_GET_STA_LIST)); |
| memcpy(sta_list, &info->param.sta_list, sizeof(mlan_ds_sta_list)); |
| ret = sizeof(mlan_ds_sta_list); |
| done: |
| if (status != MLAN_STATUS_PENDING) |
| kfree(ioctl_req); |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief uap bss_config handler |
| * |
| * @param dev A pointer to net_device structure |
| * @param req A pointer to ifreq structure |
| * @return 0 --success, otherwise fail |
| */ |
| static int |
| woal_priv_bss_config(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| int ret = 0; |
| mlan_ds_bss *bss = NULL; |
| mlan_ioctl_req *ioctl_req = NULL; |
| t_u32 action = 0; |
| int offset = 0; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| |
| offset = strlen(CMD_MARVELL) + strlen(PRIV_CMD_BSS_CONFIG); |
| memcpy((u8 *)&action, respbuf + offset, sizeof(action)); |
| offset += sizeof(action); |
| |
| /* Allocate an IOCTL request buffer */ |
| ioctl_req = |
| (mlan_ioctl_req *) |
| woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); |
| if (ioctl_req == NULL) { |
| ret = -ENOMEM; |
| goto done; |
| } |
| |
| bss = (mlan_ds_bss *)ioctl_req->pbuf; |
| bss->sub_command = MLAN_OID_UAP_BSS_CONFIG; |
| ioctl_req->req_id = MLAN_IOCTL_BSS; |
| if (action == 1) { |
| ioctl_req->action = MLAN_ACT_SET; |
| /* Get the BSS config from user */ |
| memcpy(&bss->param.bss_config, respbuf + offset, |
| sizeof(mlan_uap_bss_param)); |
| } else { |
| ioctl_req->action = MLAN_ACT_GET; |
| } |
| |
| status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| goto done; |
| } |
| |
| if (ioctl_req->action == MLAN_ACT_GET) { |
| memcpy(respbuf + offset, &bss->param.bss_config, |
| sizeof(mlan_uap_bss_param)); |
| } |
| ret = sizeof(mlan_uap_bss_param); |
| done: |
| if (status != MLAN_STATUS_PENDING) |
| kfree(ioctl_req); |
| LEAVE(); |
| return ret; |
| } |
| #endif |
| |
| #if defined(WIFI_DIRECT_SUPPORT) |
| #if defined(STA_SUPPORT) && defined(UAP_SUPPORT) |
| /** |
| * @brief Set/Get BSS role |
| * |
| * @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_bssrole(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| t_u32 data[1]; |
| int ret = 0; |
| int user_data_len = 0; |
| t_u8 action = MLAN_ACT_GET; |
| |
| ENTER(); |
| |
| memset((char *)data, 0, sizeof(data)); |
| if (strlen(respbuf) == (strlen(CMD_MARVELL) + strlen(PRIV_CMD_BSSROLE))) { |
| /* GET operation */ |
| user_data_len = 0; |
| } else { |
| /* SET operation */ |
| parse_arguments(respbuf + strlen(CMD_MARVELL) + |
| strlen(PRIV_CMD_BSSROLE), data, |
| ARRAY_SIZE(data), &user_data_len); |
| } |
| |
| if (user_data_len >= 2) { |
| PRINTM(MERROR, "Too many arguments\n"); |
| ret = -EINVAL; |
| goto error; |
| } |
| |
| if (user_data_len == 0) { |
| action = MLAN_ACT_GET; |
| } else { |
| if ((data[0] != MLAN_BSS_ROLE_STA && |
| data[0] != MLAN_BSS_ROLE_UAP) || |
| priv->bss_type != MLAN_BSS_TYPE_WIFIDIRECT) { |
| PRINTM(MWARN, "Invalid BSS role\n"); |
| ret = -EINVAL; |
| goto error; |
| } |
| if (data[0] == GET_BSS_ROLE(priv)) { |
| PRINTM(MWARN, "Already BSS is in desired role\n"); |
| goto done; |
| } |
| action = MLAN_ACT_SET; |
| /* Reset interface */ |
| woal_reset_intf(priv, MOAL_IOCTL_WAIT, MFALSE); |
| } |
| |
| if (MLAN_STATUS_SUCCESS != woal_bss_role_cfg(priv, |
| action, MOAL_IOCTL_WAIT, |
| (t_u8 *)data)) { |
| ret = -EFAULT; |
| goto error; |
| } |
| |
| if (user_data_len) { |
| /* Initialize private structures */ |
| woal_init_priv(priv, MOAL_IOCTL_WAIT); |
| /* Enable interfaces */ |
| netif_device_attach(priv->netdev); |
| woal_start_queue(priv->netdev); |
| } |
| |
| done: |
| memset(respbuf, 0, respbuflen); |
| respbuf[0] = (t_u8)data[0]; |
| ret = 1; |
| |
| error: |
| LEAVE(); |
| return ret; |
| } |
| #endif /* STA_SUPPORT && UAP_SUPPORT */ |
| #endif /* WIFI_DIRECT_SUPPORT && V14_FEATURE */ |
| |
| #ifdef STA_SUPPORT |
| /** |
| * @brief Set user scan |
| * |
| * @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_setuserscan(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| wlan_user_scan_cfg scan_cfg; |
| int ret = 0; |
| |
| ENTER(); |
| |
| /* Create the scan_cfg structure */ |
| memset(&scan_cfg, 0, sizeof(scan_cfg)); |
| |
| /* We expect the scan_cfg structure to be passed in respbuf */ |
| memcpy((char *)&scan_cfg, |
| respbuf + strlen(CMD_MARVELL) + strlen(PRIV_CMD_SETUSERSCAN), |
| sizeof(wlan_user_scan_cfg)); |
| |
| /* Call for scan */ |
| if (MLAN_STATUS_FAILURE == woal_do_scan(priv, &scan_cfg)) |
| ret = -EFAULT; |
| |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief Retrieve the scan response/beacon table |
| * |
| * @param respbuf A pointer to response buffer |
| * @param respbuflen Available length of response buffer |
| * @param scan_resp A pointer to mlan_scan_resp structure |
| * @param scan_start Argument |
| * |
| * @return MLAN_STATUS_SUCCESS --success, otherwise fail |
| */ |
| int |
| moal_ret_get_scan_table_ioctl(t_u8 *respbuf, t_u32 respbuflen, |
| 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 *)respbuf; |
| pcurrent = (t_u8 *)prsp_info->scan_table_entry_buf; |
| |
| pbuffer_end = respbuf + respbuflen - 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", respbuflen); |
| |
| 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 - respbuf; |
| |
| LEAVE(); |
| return ret_len; |
| } |
| |
| /** |
| * @brief Get scan table |
| * |
| * @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_getscantable(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| int ret = 0; |
| mlan_ioctl_req *req = NULL; |
| mlan_ds_scan *scan = NULL; |
| t_u32 scan_start; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| moal_handle *handle = priv->phandle; |
| |
| ENTER(); |
| |
| /* First make sure scanning is not in progress */ |
| if (handle->scan_pending_on_block == MTRUE) { |
| ret = -EAGAIN; |
| goto done; |
| } |
| |
| /* 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 */ |
| memcpy(&scan_start, |
| respbuf + strlen(CMD_MARVELL) + strlen(PRIV_CMD_GETSCANTABLE), |
| sizeof(scan_start)); |
| 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) { |
| ret = moal_ret_get_scan_table_ioctl(respbuf, respbuflen, |
| &scan->param.scan_resp, |
| scan_start); |
| } |
| done: |
| if (status != MLAN_STATUS_PENDING) |
| kfree(req); |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief Extended capabilities configuration |
| * |
| * @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_extcapcfg(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| int ret, header; |
| mlan_ioctl_req *req = NULL; |
| mlan_ds_misc_cfg *cfg = NULL; |
| IEEEtypes_Header_t *ie; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| |
| if (!respbuf) { |
| LEAVE(); |
| return 0; |
| } |
| |
| 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_EXT_CAP_CFG; |
| req->req_id = MLAN_IOCTL_MISC_CFG; |
| header = strlen(CMD_MARVELL) + strlen(PRIV_CMD_EXTCAPCFG); |
| if (strlen(respbuf) == header) |
| /* GET operation */ |
| req->action = MLAN_ACT_GET; |
| else { |
| /* SET operation */ |
| ie = (IEEEtypes_Header_t *)(respbuf + header); |
| if (ie->len > sizeof(ExtCap_t)) { |
| PRINTM(MERROR, |
| "Extended Capability lenth is invalid\n"); |
| ret = -EFAULT; |
| goto done; |
| } |
| req->action = MLAN_ACT_SET; |
| memset(&cfg->param.ext_cap, 0, sizeof(ExtCap_t)); |
| memcpy(&cfg->param.ext_cap, ie + 1, ie->len); |
| } |
| |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| goto done; |
| } |
| |
| memset(respbuf, 0, respbuflen); |
| ie = (IEEEtypes_Header_t *)respbuf; |
| ie->element_id = EXT_CAPABILITY; |
| ie->len = sizeof(ExtCap_t); |
| memcpy(ie + 1, &cfg->param.ext_cap, sizeof(ExtCap_t)); |
| |
| ret = sizeof(IEEEtypes_Header_t) + ie->len; |
| |
| done: |
| if (status != MLAN_STATUS_PENDING) |
| kfree(req); |
| LEAVE(); |
| return ret; |
| } |
| #endif |
| |
| /** |
| * @brief Set/Get deep sleep mode 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_priv_setgetdeepsleep(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| t_u32 data[2]; |
| int ret = 0; |
| int user_data_len = 0; |
| |
| ENTER(); |
| |
| if (strlen(respbuf) == |
| (strlen(CMD_MARVELL) + strlen(PRIV_CMD_DEEPSLEEP))) { |
| /* GET operation */ |
| user_data_len = 0; |
| } else { |
| /* SET operation */ |
| memset((char *)data, 0, sizeof(data)); |
| parse_arguments(respbuf + strlen(CMD_MARVELL) + |
| strlen(PRIV_CMD_DEEPSLEEP), data, |
| ARRAY_SIZE(data), &user_data_len); |
| } |
| |
| if (user_data_len >= 3) { |
| PRINTM(MERROR, "Too many arguments\n"); |
| ret = -EINVAL; |
| goto done; |
| } |
| |
| if (user_data_len == 0) { |
| if (MLAN_STATUS_SUCCESS != woal_get_deep_sleep(priv, data)) { |
| ret = -EFAULT; |
| goto done; |
| } |
| sprintf(respbuf, "%d %d", data[0], data[1]); |
| ret = strlen(respbuf) + 1; |
| } else { |
| if (data[0] == 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) { |
| ret = -EINVAL; |
| goto done; |
| } |
| } else if (data[0] == DEEP_SLEEP_ON) { |
| PRINTM(MINFO, "Enter Deep Sleep Mode\n"); |
| if (user_data_len != 2) |
| data[1] = 0; |
| ret = woal_set_deep_sleep(priv, MOAL_IOCTL_WAIT, MTRUE, |
| data[1]); |
| if (ret != MLAN_STATUS_SUCCESS) { |
| ret = -EINVAL; |
| goto done; |
| } |
| } else { |
| PRINTM(MERROR, "Unknown option = %u\n", data[0]); |
| ret = -EINVAL; |
| goto done; |
| } |
| ret = sprintf(respbuf, "OK\n") + 1; |
| } |
| |
| done: |
| LEAVE(); |
| return ret; |
| |
| } |
| |
| /** |
| * @brief Set/Get IP address 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_priv_setgetipaddr(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| mlan_ioctl_req *req = NULL; |
| mlan_ds_misc_cfg *misc = NULL; |
| int ret = 0, op_code = 0, data_length = 0, header = 0; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| |
| if (priv->bss_type != MLAN_BSS_TYPE_STA) { |
| PRINTM(MIOCTL, "Bss type[%d]: Not STA, ignore it\n", |
| priv->bss_type); |
| ret = sprintf(respbuf, "OK\n") + 1; |
| goto done; |
| } |
| |
| header = strlen(CMD_MARVELL) + strlen(PRIV_CMD_IPADDR); |
| data_length = strlen(respbuf) - header; |
| |
| 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; |
| |
| if (data_length < 1) { /* GET */ |
| req->action = MLAN_ACT_GET; |
| } else { |
| /* Make sure we have the operation argument */ |
| if (data_length > 2 && respbuf[header + 1] != ';') { |
| PRINTM(MERROR, |
| "No operation argument. Separate with ';'\n"); |
| ret = -EINVAL; |
| goto done; |
| } else { |
| respbuf[header + 1] = '\0'; |
| } |
| 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(&respbuf[header + 2], |
| MIN((IPADDR_MAX_BUF - 3), (data_length - 2)), |
| misc->param.ipaddr_cfg.ip_addr[0], ' ', NULL); |
| misc->param.ipaddr_cfg.ip_addr_num = 1; |
| misc->param.ipaddr_cfg.ip_addr_type = IPADDR_TYPE_IPV4; |
| |
| if (woal_atoi(&op_code, &respbuf[header]) != |
| MLAN_STATUS_SUCCESS) { |
| ret = -EINVAL; |
| goto done; |
| } |
| misc->param.ipaddr_cfg.op_code = (t_u32)op_code; |
| } |
| |
| req->req_id = MLAN_IOCTL_MISC_CFG; |
| misc->sub_command = MLAN_OID_MISC_IP_ADDR; |
| |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| goto done; |
| } |
| |
| if (req->action == MLAN_ACT_GET) { |
| snprintf(respbuf, 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]); |
| ret = IPADDR_MAX_BUF + 1; |
| } else { |
| ret = sprintf(respbuf, "OK\n") + 1; |
| } |
| |
| done: |
| if (status != MLAN_STATUS_PENDING) |
| kfree(req); |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief Set/Get WPS session 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_priv_setwpssession(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| mlan_ioctl_req *req = NULL; |
| mlan_ds_wps_cfg *pwps = NULL; |
| t_u32 data[1]; |
| int ret = 0; |
| int user_data_len = 0; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| |
| memset((char *)data, 0, sizeof(data)); |
| if (strlen(respbuf) == |
| (strlen(CMD_MARVELL) + strlen(PRIV_CMD_WPSSESSION))) { |
| /* GET operation */ |
| user_data_len = 0; |
| } else { |
| /* SET operation */ |
| parse_arguments(respbuf + strlen(CMD_MARVELL) + |
| strlen(PRIV_CMD_WPSSESSION), data, |
| ARRAY_SIZE(data), &user_data_len); |
| } |
| |
| if (user_data_len > 1) { |
| PRINTM(MERROR, "Too many arguments\n"); |
| ret = -EINVAL; |
| 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 (data[0] == 1) |
| pwps->param.wps_session = MLAN_WPS_CFG_SESSION_START; |
| else |
| pwps->param.wps_session = MLAN_WPS_CFG_SESSION_END; |
| |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| goto done; |
| } |
| |
| ret = sprintf(respbuf, "OK\n") + 1; |
| done: |
| if (status != MLAN_STATUS_PENDING) |
| kfree(req); |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief Get OTP user data |
| * |
| * @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_otpuserdata(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| int data[1]; |
| int user_data_len = 0; |
| mlan_ioctl_req *req = NULL; |
| mlan_ds_misc_cfg *misc = NULL; |
| mlan_ds_misc_otp_user_data *otp = NULL; |
| int ret = 0; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| |
| if (strlen(respbuf) == |
| (strlen(CMD_MARVELL) + strlen(PRIV_CMD_OTPUSERDATA))) { |
| PRINTM(MERROR, "Invalid number of arguments\n"); |
| ret = -EINVAL; |
| goto done; |
| } |
| memset((char *)data, 0, sizeof(data)); |
| parse_arguments(respbuf + strlen(CMD_MARVELL) + |
| strlen(PRIV_CMD_OTPUSERDATA), data, ARRAY_SIZE(data), |
| &user_data_len); |
| |
| if (user_data_len != 1) { |
| PRINTM(MERROR, "Invalid number of arguments\n"); |
| ret = -EINVAL; |
| goto done; |
| } |
| |
| req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); |
| if (req == NULL) { |
| ret = -ENOMEM; |
| goto done; |
| } |
| req->action = MLAN_ACT_GET; |
| req->req_id = MLAN_IOCTL_MISC_CFG; |
| |
| misc = (mlan_ds_misc_cfg *)req->pbuf; |
| misc->sub_command = MLAN_OID_MISC_OTP_USER_DATA; |
| misc->param.otp_user_data.user_data_length = data[0]; |
| |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| goto done; |
| } |
| otp = (mlan_ds_misc_otp_user_data *)req->pbuf; |
| |
| if (req->action == MLAN_ACT_GET) { |
| ret = MIN(otp->user_data_length, data[0]); |
| memcpy(respbuf, otp->user_data, ret); |
| } |
| |
| done: |
| if (status != MLAN_STATUS_PENDING) |
| kfree(req); |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief Set / Get country code |
| * |
| * @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_set_get_countrycode(moal_private *priv, t_u8 *respbuf, |
| t_u32 respbuflen) |
| { |
| int ret = 0; |
| /* char data[COUNTRY_CODE_LEN] = {0, 0, 0}; */ |
| int header = 0, data_length = 0; /* wrq->u.data.length; */ |
| mlan_ioctl_req *req = NULL; |
| mlan_ds_misc_cfg *pcfg_misc = NULL; |
| mlan_ds_misc_country_code *country_code = NULL; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| |
| header = strlen(CMD_MARVELL) + strlen(PRIV_CMD_COUNTRYCODE); |
| data_length = strlen(respbuf) - header; |
| |
| if (data_length > COUNTRY_CODE_LEN) { |
| PRINTM(MERROR, "Invalid argument!\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 */ |
| pcfg_misc = (mlan_ds_misc_cfg *)req->pbuf; |
| country_code = &pcfg_misc->param.country_code; |
| pcfg_misc->sub_command = MLAN_OID_MISC_COUNTRY_CODE; |
| req->req_id = MLAN_IOCTL_MISC_CFG; |
| |
| if (data_length <= 1) { |
| req->action = MLAN_ACT_GET; |
| } else { |
| memset(country_code->country_code, 0, COUNTRY_CODE_LEN); |
| memcpy(country_code->country_code, respbuf + header, |
| COUNTRY_CODE_LEN); |
| req->action = MLAN_ACT_SET; |
| } |
| |
| /* Send IOCTL request to MLAN */ |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| goto done; |
| } |
| |
| if (req->action == MLAN_ACT_GET) { |
| ret = data_length = COUNTRY_CODE_LEN; |
| memset(respbuf + header, 0, COUNTRY_CODE_LEN); |
| memcpy(respbuf, country_code->country_code, COUNTRY_CODE_LEN); |
| } |
| |
| done: |
| if (status != MLAN_STATUS_PENDING) |
| kfree(req); |
| |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief Set/Get TCP Ack enhancement 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_priv_setgettcpackenh(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| t_u32 data[1]; |
| int ret = 0; |
| int user_data_len = 0; |
| |
| ENTER(); |
| |
| if (strlen(respbuf) == |
| (strlen(CMD_MARVELL) + strlen(PRIV_CMD_TCPACKENH))) { |
| /* GET operation */ |
| user_data_len = 0; |
| } else { |
| /* SET operation */ |
| memset((char *)data, 0, sizeof(data)); |
| parse_arguments(respbuf + strlen(CMD_MARVELL) + |
| strlen(PRIV_CMD_TCPACKENH), data, |
| ARRAY_SIZE(data), &user_data_len); |
| } |
| |
| if (user_data_len >= 2) { |
| PRINTM(MERROR, "Too many arguments\n"); |
| ret = -EINVAL; |
| goto done; |
| } |
| |
| if (user_data_len == 0) { |
| /* get operation */ |
| respbuf[0] = priv->enable_tcp_ack_enh; |
| } else { |
| /* set operation */ |
| if (data[0] == MTRUE) { |
| PRINTM(MINFO, "Enabling TCP Ack enhancement\n"); |
| priv->enable_tcp_ack_enh = MTRUE; |
| } else if (data[0] == MFALSE) { |
| PRINTM(MINFO, "Disabling TCP Ack enhancement\n"); |
| priv->enable_tcp_ack_enh = MFALSE; |
| /* release the tcp sessions if any */ |
| woal_flush_tcp_sess_queue(priv); |
| } else { |
| PRINTM(MERROR, "Unknown option = %u\n", data[0]); |
| ret = -EINVAL; |
| goto done; |
| } |
| respbuf[0] = priv->enable_tcp_ack_enh; |
| } |
| ret = 1; |
| |
| done: |
| LEAVE(); |
| return ret; |
| |
| } |
| |
| #ifdef REASSOCIATION |
| /** |
| * @brief Set Asynced ESSID |
| * |
| * @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_assocessid(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| mlan_802_11_ssid req_ssid; |
| mlan_ssid_bssid ssid_bssid; |
| moal_handle *handle = priv->phandle; |
| int ret = 0; |
| t_u8 *essid_ptr; |
| t_u16 essid_length = 0; |
| |
| ENTER(); |
| |
| if (strlen(respbuf) == |
| (strlen(CMD_MARVELL) + strlen(PRIV_CMD_ASSOCESSID))) { |
| PRINTM(MERROR, "No argument, invalid operation!\n"); |
| ret = -EINVAL; |
| LEAVE(); |
| return ret; |
| } else { |
| /* SET operation */ |
| essid_ptr = |
| respbuf + (strlen(CMD_MARVELL) + |
| strlen(PRIV_CMD_ASSOCESSID)); |
| essid_length = strlen(essid_ptr); |
| } |
| |
| /* Cancel re-association */ |
| priv->reassoc_required = MFALSE; |
| |
| if (MOAL_ACQ_SEMAPHORE_BLOCK(&handle->reassoc_sem)) { |
| PRINTM(MERROR, "Acquire semaphore error, woal_set_essid\n"); |
| ret = -EBUSY; |
| LEAVE(); |
| return ret; |
| } |
| |
| /* Check the size of the string */ |
| if (essid_length > MW_ESSID_MAX_SIZE + 1) { |
| ret = -E2BIG; |
| goto setessid_ret; |
| } |
| if (priv->scan_type == MLAN_SCAN_TYPE_PASSIVE) |
| woal_set_scan_type(priv, MLAN_SCAN_TYPE_ACTIVE); |
| memset(&req_ssid, 0, sizeof(mlan_802_11_ssid)); |
| memset(&ssid_bssid, 0, sizeof(mlan_ssid_bssid)); |
| |
| req_ssid.ssid_len = essid_length; |
| |
| /* Set the SSID */ |
| memcpy(req_ssid.ssid, essid_ptr, |
| MIN(req_ssid.ssid_len, MLAN_MAX_SSID_LENGTH)); |
| if (!req_ssid.ssid_len || (MFALSE == woal_ssid_valid(&req_ssid))) { |
| PRINTM(MERROR, "Invalid SSID - aborting set_essid\n"); |
| ret = -EINVAL; |
| goto setessid_ret; |
| } |
| |
| PRINTM(MINFO, "Requested new SSID = %s\n", (char *)req_ssid.ssid); |
| memcpy(&ssid_bssid.ssid, &req_ssid, sizeof(mlan_802_11_ssid)); |
| if (MTRUE == woal_is_connected(priv, &ssid_bssid)) { |
| PRINTM(MIOCTL, "Already connect to the network\n"); |
| ret = sprintf(respbuf, |
| "Has already connected to this ESSID!\n") + 1; |
| goto setessid_ret; |
| } |
| |
| memcpy(&priv->prev_ssid_bssid.ssid, &req_ssid, |
| sizeof(mlan_802_11_ssid)); |
| /* disconnect before driver assoc */ |
| woal_disconnect(priv, MOAL_IOCTL_WAIT, NULL); |
| priv->set_asynced_essid_flag = MTRUE; |
| priv->reassoc_required = MTRUE; |
| priv->phandle->is_reassoc_timer_set = MTRUE; |
| woal_mod_timer(&priv->phandle->reassoc_timer, 0); |
| ret = sprintf(respbuf, "%s\n", essid_ptr) + 1; |
| |
| setessid_ret: |
| if (priv->scan_type == MLAN_SCAN_TYPE_PASSIVE) |
| woal_set_scan_type(priv, MLAN_SCAN_TYPE_PASSIVE); |
| MOAL_REL_SEMAPHORE(&handle->reassoc_sem); |
| LEAVE(); |
| return ret; |
| } |
| #endif |
| |
| /** |
| * @brief Get wakeup reason |
| * |
| * @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_getwakeupreason(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| mlan_ioctl_req *req = NULL; |
| mlan_ds_pm_cfg *pm_cfg = NULL; |
| t_u32 data; |
| int ret = 0; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| |
| if (strlen(respbuf) == |
| (strlen(CMD_MARVELL) + strlen(PRIV_CMD_WAKEUPREASON))) { |
| /* GET operation */ |
| req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_pm_cfg)); |
| if (req == NULL) { |
| LEAVE(); |
| return -ENOMEM; |
| } |
| |
| pm_cfg = (mlan_ds_pm_cfg *)req->pbuf; |
| pm_cfg->sub_command = MLAN_OID_PM_HS_WAKEUP_REASON; |
| req->req_id = MLAN_IOCTL_PM_CFG; |
| req->action = MLAN_ACT_GET; |
| |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| if (status != MLAN_STATUS_PENDING) |
| kfree(req); |
| goto done; |
| } else { |
| data = pm_cfg->param.wakeup_reason.hs_wakeup_reason; |
| sprintf(respbuf, " %d", data); |
| ret = strlen(respbuf) + 1; |
| kfree(req); |
| } |
| } else { |
| PRINTM(MERROR, "Not need argument, invalid operation!\n"); |
| ret = -EINVAL; |
| goto done; |
| } |
| |
| done: |
| LEAVE(); |
| return ret; |
| |
| } |
| |
| #ifdef STA_SUPPORT |
| /** |
| * @brief Set / Get listen interval |
| * |
| * @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_set_get_listeninterval(moal_private *priv, t_u8 *respbuf, |
| t_u32 respbuflen) |
| { |
| int data[1]; |
| int user_data_len = 0; |
| int ret = 0; |
| mlan_ioctl_req *req = NULL; |
| mlan_ds_bss *pcfg_bss = NULL; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| |
| if (strlen(respbuf) == |
| (strlen(CMD_MARVELL) + strlen(PRIV_CMD_LISTENINTERVAL))) { |
| /* GET operation */ |
| user_data_len = 0; |
| } else { |
| /* SET operation */ |
| memset((char *)data, 0, sizeof(data)); |
| parse_arguments(respbuf + strlen(CMD_MARVELL) + |
| strlen(PRIV_CMD_LISTENINTERVAL), data, |
| ARRAY_SIZE(data), &user_data_len); |
| } |
| |
| if (user_data_len > 1) { |
| PRINTM(MERROR, "Too many arguments\n"); |
| ret = -EINVAL; |
| goto done; |
| } |
| |
| /* 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 */ |
| pcfg_bss = (mlan_ds_bss *)req->pbuf; |
| pcfg_bss->sub_command = MLAN_OID_BSS_LISTEN_INTERVAL; |
| req->req_id = MLAN_IOCTL_BSS; |
| |
| if (user_data_len) { |
| pcfg_bss->param.listen_interval = (t_u16)data[0]; |
| req->action = MLAN_ACT_SET; |
| } else { |
| req->action = MLAN_ACT_GET; |
| } |
| |
| /* Send IOCTL request to MLAN */ |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| goto done; |
| } |
| if (req->action == MLAN_ACT_GET) { |
| sprintf(respbuf, "%d", pcfg_bss->param.listen_interval); |
| ret = strlen(respbuf) + 1; |
| } else { |
| ret = sprintf(respbuf, "OK\n") + 1; |
| } |
| |
| done: |
| if (status != MLAN_STATUS_PENDING) |
| kfree(req); |
| |
| LEAVE(); |
| return ret; |
| } |
| #endif |
| |
| #ifdef DEBUG_LEVEL1 |
| /** |
| * @brief Set / Get driver debug level |
| * |
| * @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_set_get_drvdbg(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| int data[4]; |
| int user_data_len = 0; |
| int ret = 0; |
| |
| ENTER(); |
| |
| if (strlen(respbuf) == (strlen(CMD_MARVELL) + strlen(PRIV_CMD_DRVDBG))) { |
| /* GET operation */ |
| user_data_len = 0; |
| } else { |
| /* SET operation */ |
| memset((char *)data, 0, sizeof(data)); |
| parse_arguments(respbuf + strlen(CMD_MARVELL) + |
| strlen(PRIV_CMD_DRVDBG), data, ARRAY_SIZE(data), |
| &user_data_len); |
| } |
| |
| if (user_data_len > 1) { |
| PRINTM(MERROR, "Too many arguments\n"); |
| ret = -EINVAL; |
| goto done; |
| } |
| |
| if (user_data_len) { |
| /* Get the driver debug bit masks from user */ |
| drvdbg = data[0]; |
| /* Set the driver debug bit masks into mlan */ |
| if (woal_set_drvdbg(priv, drvdbg)) { |
| PRINTM(MERROR, "Set drvdbg failed!\n"); |
| ret = MLAN_STATUS_FAILURE; |
| goto done; |
| } |
| } |
| |
| ret = sizeof(drvdbg); |
| |
| memcpy(respbuf, &drvdbg, sizeof(drvdbg)); |
| |
| printk(KERN_ALERT "drvdbg = 0x%08x\n", drvdbg); |
| #ifdef DEBUG_LEVEL2 |
| printk(KERN_ALERT "MINFO (%08x) %s\n", MINFO, |
| (drvdbg & MINFO) ? "X" : ""); |
| printk(KERN_ALERT "MWARN (%08x) %s\n", MWARN, |
| (drvdbg & MWARN) ? "X" : ""); |
| printk(KERN_ALERT "MENTRY (%08x) %s\n", MENTRY, |
| (drvdbg & MENTRY) ? "X" : ""); |
| #endif |
| printk(KERN_ALERT "MMPA_D (%08x) %s\n", MMPA_D, |
| (drvdbg & MMPA_D) ? "X" : ""); |
| printk(KERN_ALERT "MIF_D (%08x) %s\n", MIF_D, |
| (drvdbg & MIF_D) ? "X" : ""); |
| printk(KERN_ALERT "MFW_D (%08x) %s\n", MFW_D, |
| (drvdbg & MFW_D) ? "X" : ""); |
| printk(KERN_ALERT "MEVT_D (%08x) %s\n", MEVT_D, |
| (drvdbg & MEVT_D) ? "X" : ""); |
| printk(KERN_ALERT "MCMD_D (%08x) %s\n", MCMD_D, |
| (drvdbg & MCMD_D) ? "X" : ""); |
| printk(KERN_ALERT "MDAT_D (%08x) %s\n", MDAT_D, |
| (drvdbg & MDAT_D) ? "X" : ""); |
| printk(KERN_ALERT "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" : ""); |
| |
| done: |
| LEAVE(); |
| return ret; |
| } |
| |
| #endif |
| |
| /** |
| * @brief Set/Get Host Sleep configuration |
| * |
| * @param priv A pointer to moal_private structure |
| * @param respbuf A pointer to response buffer |
| * @param respbuflen Available length of response buffer |
| * @param invoke_hostcmd MTRUE --invoke HostCmd, otherwise MFALSE |
| * |
| * @return 0 --success, otherwise fail |
| */ |
| int |
| woal_priv_hscfg(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen, |
| BOOLEAN invoke_hostcmd) |
| { |
| int data[3]; |
| int user_data_len = 0; |
| int ret = 0; |
| mlan_ds_hs_cfg hscfg; |
| t_u16 action; |
| mlan_bss_info bss_info; |
| int is_negative = MFALSE; |
| t_u8 *arguments = NULL; |
| |
| ENTER(); |
| |
| memset(data, 0, sizeof(data)); |
| memset(&hscfg, 0, sizeof(mlan_ds_hs_cfg)); |
| |
| if (strlen(respbuf) == (strlen(CMD_MARVELL) + strlen(PRIV_CMD_HSCFG))) { |
| /* GET operation */ |
| user_data_len = 0; |
| } else { |
| /* SET operation */ |
| memset((char *)data, 0, sizeof(data)); |
| arguments = |
| respbuf + strlen(CMD_MARVELL) + strlen(PRIV_CMD_HSCFG); |
| if (*arguments == '-') { |
| is_negative = MTRUE; |
| arguments += 1; |
| } |
| parse_arguments(arguments, data, ARRAY_SIZE(data), |
| &user_data_len); |
| |
| if (is_negative == MTRUE) { |
| if (data[0] == 1) { |
| data[0] = -1; |
| } else { |
| PRINTM(MERROR, "Invalid arguments\n"); |
| ret = -EINVAL; |
| goto done; |
| } |
| } |
| } |
| |
| if (sizeof(int) * user_data_len > sizeof(data)) { |
| PRINTM(MERROR, "Too many arguments\n"); |
| LEAVE(); |
| return -EINVAL; |
| } |
| |
| if (user_data_len == 0) { |
| action = MLAN_ACT_GET; |
| } else { |
| if (user_data_len >= 1 && user_data_len <= 3) { |
| action = MLAN_ACT_SET; |
| } else { |
| PRINTM(MERROR, "Invalid arguments\n"); |
| ret = -EINVAL; |
| goto done; |
| } |
| } |
| |
| /* HS config is blocked if HS is already activated */ |
| if (user_data_len && |
| (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 (user_data_len >= 1 && user_data_len < 3) { |
| woal_set_get_hs_params(priv, MLAN_ACT_GET, MOAL_IOCTL_WAIT, |
| &hscfg); |
| } |
| |
| if (user_data_len) |
| hscfg.conditions = data[0]; |
| if (user_data_len >= 2) |
| hscfg.gpio = data[1]; |
| if (user_data_len == 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) { |
| /* Return the current driver host sleep configurations */ |
| memcpy(respbuf, &hscfg, sizeof(mlan_ds_hs_cfg)); |
| ret = sizeof(mlan_ds_hs_cfg); |
| } |
| done: |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief Set Host Sleep parameters |
| * |
| * @param priv A pointer to moal_private structure |
| * @param respbuf A pointer to response buffer |
| * @param respbuflen Available length of response buffer |
| * |
| * @return 0 --success, otherwise fail |
| */ |
| int |
| woal_priv_hssetpara(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| int data[3]; |
| int user_data_len = 0; |
| int ret = 0; |
| |
| ENTER(); |
| |
| if (strlen(respbuf) == |
| (strlen(CMD_MARVELL) + strlen(PRIV_CMD_HSSETPARA))) { |
| PRINTM(MERROR, "Invalid arguments\n"); |
| ret = -EINVAL; |
| goto done; |
| } else { |
| /* SET operation */ |
| memset((char *)data, 0, sizeof(data)); |
| parse_arguments(respbuf + strlen(CMD_MARVELL) + |
| strlen(PRIV_CMD_HSSETPARA), data, |
| ARRAY_SIZE(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 >= 1 && user_data_len <= 3) { |
| sprintf(respbuf, "%s%s%s", CMD_MARVELL, PRIV_CMD_HSCFG, |
| respbuf + (strlen(CMD_MARVELL) + |
| strlen(PRIV_CMD_HSSETPARA))); |
| respbuflen = strlen(respbuf); |
| ret = woal_priv_hscfg(priv, respbuf, respbuflen, MFALSE); |
| goto done; |
| } |
| done: |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief Set/Get scan configuration parameters |
| * |
| * @param priv A pointer to moal_private structure |
| * @param respbuf A pointer to response buffer |
| * @param respbuflen Available length of response buffer |
| * |
| * @return 0 --success, otherwise fail |
| */ |
| int |
| woal_priv_set_get_scancfg(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| int ret = 0; |
| int user_data_len = 0; |
| int arg_len = 7; |
| int data[arg_len]; |
| mlan_ds_scan *scan = NULL; |
| mlan_ioctl_req *req = NULL; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| |
| memset(data, 0, sizeof(data)); |
| if (strlen(respbuf) == (strlen(CMD_MARVELL) + strlen(PRIV_CMD_SCANCFG))) { |
| /* GET operation */ |
| user_data_len = 0; |
| } else { |
| /* SET operation */ |
| memset((char *)data, 0, sizeof(data)); |
| parse_arguments(respbuf + strlen(CMD_MARVELL) + |
| strlen(PRIV_CMD_SCANCFG), data, |
| ARRAY_SIZE(data), &user_data_len); |
| } |
| |
| if (sizeof(int) * user_data_len > sizeof(data)) { |
| PRINTM(MERROR, "Too many arguments\n"); |
| LEAVE(); |
| return -EINVAL; |
| } |
| |
| req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_scan)); |
| if (req == NULL) { |
| ret = -ENOMEM; |
| goto done; |
| } |
| scan = (mlan_ds_scan *)req->pbuf; |
| scan->sub_command = MLAN_OID_SCAN_CONFIG; |
| req->req_id = MLAN_IOCTL_SCAN; |
| |
| if (user_data_len) { |
| 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; |
| |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| goto done; |
| } |
| if (!user_data_len) { |
| memcpy(respbuf, &scan->param.scan_cfg, sizeof(mlan_scan_cfg)); |
| ret = sizeof(mlan_scan_cfg); |
| } |
| done: |
| if (status != MLAN_STATUS_PENDING) |
| kfree(req); |
| LEAVE(); |
| return ret; |
| } |
| |
| #ifdef STA_SUPPORT |
| /** |
| * @brief Set AP settings |
| * |
| * @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 if successful, otherwise fail |
| */ |
| static int |
| woal_priv_set_ap(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| int ret = 0; |
| t_u8 *data_ptr; |
| const t_u8 bcast[MLAN_MAC_ADDR_LENGTH] = |
| { 255, 255, 255, 255, 255, 255 }; |
| const t_u8 zero_mac[MLAN_MAC_ADDR_LENGTH] = { 0, 0, 0, 0, 0, 0 }; |
| mlan_ssid_bssid ssid_bssid; |
| mlan_bss_info bss_info; |
| struct mwreq *mwr; |
| struct sockaddr *awrq; |
| |
| ENTER(); |
| data_ptr = respbuf + (strlen(CMD_MARVELL) + strlen(PRIV_CMD_SET_AP)); |
| |
| mwr = (struct mwreq *)data_ptr; |
| |
| if (mwr->u.ap_addr.sa_family != ARPHRD_ETHER) { |
| ret = -EINVAL; |
| goto done; |
| } |
| |
| awrq = (struct sockaddr *)&(mwr->u.ap_addr); |
| |
| PRINTM(MINFO, "ASSOC: WAP: sa_data: " MACSTR "\n", |
| MAC2STR((t_u8 *)awrq->sa_data)); |
| |
| if (MLAN_STATUS_SUCCESS != |
| woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info)) { |
| ret = -EFAULT; |
| goto done; |
| } |
| #ifdef REASSOCIATION |
| /* Cancel re-association */ |
| priv->reassoc_required = MFALSE; |
| #endif |
| |
| /* zero_mac means disconnect */ |
| if (!memcmp(zero_mac, awrq->sa_data, MLAN_MAC_ADDR_LENGTH)) { |
| woal_disconnect(priv, MOAL_IOCTL_WAIT, NULL); |
| goto done; |
| } |
| |
| /* Broadcast MAC means search for best network */ |
| memset(&ssid_bssid, 0, sizeof(mlan_ssid_bssid)); |
| |
| if (memcmp(bcast, awrq->sa_data, MLAN_MAC_ADDR_LENGTH)) { |
| /* Check if we are already assoicated to the AP */ |
| if (bss_info.media_connected == MTRUE) { |
| if (!memcmp(awrq->sa_data, &bss_info.bssid, ETH_ALEN)) |
| goto done; |
| } |
| memcpy(&ssid_bssid.bssid, awrq->sa_data, ETH_ALEN); |
| } |
| |
| if (MLAN_STATUS_SUCCESS != woal_find_best_network(priv, |
| MOAL_IOCTL_WAIT, |
| &ssid_bssid)) { |
| PRINTM(MERROR, |
| "ASSOC: WAP: MAC address not found in BSSID List\n"); |
| ret = -ENETUNREACH; |
| goto done; |
| } |
| /* Zero SSID implies use BSSID to connect */ |
| memset(&ssid_bssid.ssid, 0, sizeof(mlan_802_11_ssid)); |
| if (MLAN_STATUS_SUCCESS != woal_bss_start(priv, |
| MOAL_IOCTL_WAIT, |
| &ssid_bssid)) { |
| ret = -EFAULT; |
| goto done; |
| } |
| #ifdef REASSOCIATION |
| memset(&bss_info, 0, sizeof(bss_info)); |
| if (MLAN_STATUS_SUCCESS != woal_get_bss_info(priv, |
| MOAL_IOCTL_WAIT, |
| &bss_info)) { |
| ret = -EFAULT; |
| goto done; |
| } |
| memcpy(&priv->prev_ssid_bssid.ssid, &bss_info.ssid, |
| sizeof(mlan_802_11_ssid)); |
| memcpy(&priv->prev_ssid_bssid.bssid, &bss_info.bssid, |
| MLAN_MAC_ADDR_LENGTH); |
| #endif /* REASSOCIATION */ |
| |
| done: |
| LEAVE(); |
| return ret; |
| |
| } |
| #endif |
| |
| /** |
| * @brief Set BSS mode |
| * |
| * @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 if successful, otherwise fail |
| */ |
| static int |
| woal_priv_set_bss_mode(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| int ret = 0; |
| mlan_ds_bss *bss = NULL; |
| mlan_ioctl_req *req = NULL; |
| struct mwreq *mwr; |
| t_u8 *data_ptr; |
| t_u32 mode; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| |
| data_ptr = |
| respbuf + (strlen(CMD_MARVELL) + strlen(PRIV_CMD_SET_BSS_MODE)); |
| |
| mwr = (struct mwreq *)data_ptr; |
| mode = mwr->u.mode; |
| 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_BSS_MODE; |
| req->req_id = MLAN_IOCTL_BSS; |
| req->action = MLAN_ACT_SET; |
| |
| switch (mode) { |
| case MW_MODE_INFRA: |
| bss->param.bss_mode = MLAN_BSS_MODE_INFRA; |
| break; |
| case MW_MODE_ADHOC: |
| bss->param.bss_mode = MLAN_BSS_MODE_IBSS; |
| break; |
| case MW_MODE_AUTO: |
| bss->param.bss_mode = MLAN_BSS_MODE_AUTO; |
| break; |
| default: |
| ret = -EINVAL; |
| break; |
| } |
| if (ret) |
| goto done; |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| goto done; |
| } |
| done: |
| if (status != MLAN_STATUS_PENDING) |
| kfree(req); |
| LEAVE(); |
| return ret; |
| } |
| |
| #ifdef STA_SUPPORT |
| /** |
| * @brief Set power management |
| * |
| * @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 if successful, otherwise fail |
| */ |
| static int |
| woal_priv_set_power(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| struct mwreq *mwr; |
| t_u8 *data_ptr; |
| int ret = 0, disabled; |
| |
| ENTER(); |
| |
| if (hw_test) { |
| PRINTM(MIOCTL, "block set power in hw_test mode\n"); |
| LEAVE(); |
| return ret; |
| } |
| |
| data_ptr = respbuf + (strlen(CMD_MARVELL) + strlen(PRIV_CMD_SET_POWER)); |
| |
| mwr = (struct mwreq *)data_ptr; |
| disabled = mwr->u.power.disabled; |
| |
| if (MLAN_STATUS_SUCCESS != woal_set_get_power_mgmt(priv, |
| MLAN_ACT_SET, |
| &disabled, |
| mwr->u.power.flags, |
| MOAL_IOCTL_WAIT)) { |
| return -EFAULT; |
| } |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief Set essid |
| * |
| * @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 if successful, otherwise fail |
| */ |
| static int |
| woal_priv_set_essid(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| mlan_802_11_ssid req_ssid; |
| mlan_ssid_bssid ssid_bssid; |
| #ifdef REASSOCIATION |
| moal_handle *handle = priv->phandle; |
| mlan_bss_info bss_info; |
| #endif |
| int ret = 0; |
| t_u32 mode = 0; |
| struct mwreq *mwr; |
| t_u8 *data_ptr; |
| |
| ENTER(); |
| |
| data_ptr = respbuf + (strlen(CMD_MARVELL) + strlen(PRIV_CMD_SET_ESSID)); |
| |
| mwr = (struct mwreq *)data_ptr; |
| |
| #ifdef REASSOCIATION |
| /* Cancel re-association */ |
| priv->reassoc_required = MFALSE; |
| |
| if (MOAL_ACQ_SEMAPHORE_BLOCK(&handle->reassoc_sem)) { |
| PRINTM(MERROR, "Acquire semaphore error, woal_set_essid\n"); |
| LEAVE(); |
| return -EBUSY; |
| } |
| #endif /* REASSOCIATION */ |
| |
| /* Check the size of the string */ |
| if (mwr->u.essid.length > MW_ESSID_MAX_SIZE + 1) { |
| ret = -E2BIG; |
| goto setessid_ret; |
| } |
| if (priv->scan_type == MLAN_SCAN_TYPE_PASSIVE) |
| woal_set_scan_type(priv, MLAN_SCAN_TYPE_ACTIVE); |
| memset(&req_ssid, 0, sizeof(mlan_802_11_ssid)); |
| memset(&ssid_bssid, 0, sizeof(mlan_ssid_bssid)); |
| |
| req_ssid.ssid_len = mwr->u.essid.length; |
| |
| /* Check if we asked for 'any' or 'particular' */ |
| if (!mwr->u.essid.flags) { |
| #ifdef REASSOCIATION |
| if (!req_ssid.ssid_len) { |
| memset(&priv->prev_ssid_bssid.ssid, 0x00, |
| sizeof(mlan_802_11_ssid)); |
| memset(&priv->prev_ssid_bssid.bssid, 0x00, |
| MLAN_MAC_ADDR_LENGTH); |
| goto setessid_ret; |
| } |
| #endif |
| /* Do normal SSID scanning */ |
| if (MLAN_STATUS_SUCCESS != |
| woal_request_scan(priv, MOAL_IOCTL_WAIT, NULL)) { |
| ret = -EFAULT; |
| goto setessid_ret; |
| } |
| } else { |
| /* Set the SSID */ |
| memcpy(req_ssid.ssid, mwr->u.essid.pointer, |
| MIN(req_ssid.ssid_len, MLAN_MAX_SSID_LENGTH)); |
| if (!req_ssid.ssid_len || |
| (MFALSE == woal_ssid_valid(&req_ssid))) { |
| PRINTM(MERROR, "Invalid SSID - aborting set_essid\n"); |
| ret = -EINVAL; |
| goto setessid_ret; |
| } |
| |
| PRINTM(MINFO, "Requested new SSID = %s\n", |
| (char *)req_ssid.ssid); |
| memcpy(&ssid_bssid.ssid, &req_ssid, sizeof(mlan_802_11_ssid)); |
| if (MTRUE == woal_is_connected(priv, &ssid_bssid)) { |
| PRINTM(MIOCTL, "Already connect to the network\n"); |
| goto setessid_ret; |
| } |
| |
| if (mwr->u.essid.flags != 0xFFFF) { |
| if (MLAN_STATUS_SUCCESS != |
| woal_find_essid(priv, &ssid_bssid, |
| MOAL_IOCTL_WAIT)) { |
| /* Do specific SSID scanning */ |
| if (MLAN_STATUS_SUCCESS != |
| woal_request_scan(priv, MOAL_IOCTL_WAIT, |
| &req_ssid)) { |
| ret = -EFAULT; |
| goto setessid_ret; |
| } |
| } |
| } |
| |
| } |
| |
| mode = woal_get_mode(priv, MOAL_IOCTL_WAIT); |
| |
| if (mode != MW_MODE_ADHOC) { |
| if (MLAN_STATUS_SUCCESS != |
| woal_find_best_network(priv, MOAL_IOCTL_WAIT, |
| &ssid_bssid)) { |
| ret = -EFAULT; |
| goto setessid_ret; |
| } |
| } else if (MLAN_STATUS_SUCCESS != |
| woal_find_best_network(priv, MOAL_IOCTL_WAIT, &ssid_bssid)) |
| /* Adhoc start, Check the channel command */ |
| woal_11h_channel_check_ioctl(priv, MOAL_IOCTL_WAIT); |
| |
| /* Connect to BSS by ESSID */ |
| memset(&ssid_bssid.bssid, 0, MLAN_MAC_ADDR_LENGTH); |
| |
| if (MLAN_STATUS_SUCCESS != woal_bss_start(priv, |
| MOAL_IOCTL_WAIT, |
| &ssid_bssid)) { |
| ret = -EFAULT; |
| goto setessid_ret; |
| } |
| #ifdef REASSOCIATION |
| memset(&bss_info, 0, sizeof(bss_info)); |
| if (MLAN_STATUS_SUCCESS != woal_get_bss_info(priv, |
| MOAL_IOCTL_WAIT, |
| &bss_info)) { |
| ret = -EFAULT; |
| goto setessid_ret; |
| } |
| memcpy(&priv->prev_ssid_bssid.ssid, &bss_info.ssid, |
| sizeof(mlan_802_11_ssid)); |
| memcpy(&priv->prev_ssid_bssid.bssid, &bss_info.bssid, |
| MLAN_MAC_ADDR_LENGTH); |
| #endif /* REASSOCIATION */ |
| |
| setessid_ret: |
| if (priv->scan_type == MLAN_SCAN_TYPE_PASSIVE) |
| woal_set_scan_type(priv, MLAN_SCAN_TYPE_PASSIVE); |
| #ifdef REASSOCIATION |
| MOAL_REL_SEMAPHORE(&handle->reassoc_sem); |
| #endif |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief Set authentication mode parameters |
| * |
| * @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 if successful, otherwise fail |
| */ |
| static int |
| woal_priv_set_auth(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| struct mwreq *mwr; |
| t_u8 *data_ptr; |
| int ret = 0; |
| t_u32 auth_mode = 0; |
| t_u32 encrypt_mode = 0; |
| |
| ENTER(); |
| |
| data_ptr = respbuf + (strlen(CMD_MARVELL) + strlen(PRIV_CMD_SET_AUTH)); |
| |
| mwr = (struct mwreq *)data_ptr; |
| |
| switch (mwr->u.param.flags & MW_AUTH_INDEX) { |
| case MW_AUTH_CIPHER_PAIRWISE: |
| case MW_AUTH_CIPHER_GROUP: |
| if (mwr->u.param.value & MW_AUTH_CIPHER_NONE) |
| encrypt_mode = MLAN_ENCRYPTION_MODE_NONE; |
| else if (mwr->u.param.value & MW_AUTH_CIPHER_WEP40) |
| encrypt_mode = MLAN_ENCRYPTION_MODE_WEP40; |
| else if (mwr->u.param.value & MW_AUTH_CIPHER_WEP104) |
| encrypt_mode = MLAN_ENCRYPTION_MODE_WEP104; |
| else if (mwr->u.param.value & MW_AUTH_CIPHER_TKIP) |
| encrypt_mode = MLAN_ENCRYPTION_MODE_TKIP; |
| else if (mwr->u.param.value & MW_AUTH_CIPHER_CCMP) |
| encrypt_mode = MLAN_ENCRYPTION_MODE_CCMP; |
| if (MLAN_STATUS_SUCCESS != |
| woal_set_encrypt_mode(priv, MOAL_IOCTL_WAIT, encrypt_mode)) |
| ret = -EFAULT; |
| break; |
| case MW_AUTH_80211_AUTH_ALG: |
| switch (mwr->u.param.value) { |
| case MW_AUTH_ALG_SHARED_KEY: |
| PRINTM(MINFO, "Auth mode shared key!\n"); |
| auth_mode = MLAN_AUTH_MODE_SHARED; |
| break; |
| case MW_AUTH_ALG_LEAP: |
| PRINTM(MINFO, "Auth mode LEAP!\n"); |
| auth_mode = MLAN_AUTH_MODE_NETWORKEAP; |
| break; |
| case MW_AUTH_ALG_OPEN_SYSTEM: |
| PRINTM(MINFO, "Auth mode open!\n"); |
| auth_mode = MLAN_AUTH_MODE_OPEN; |
| break; |
| case MW_AUTH_ALG_SHARED_KEY | MW_AUTH_ALG_OPEN_SYSTEM: |
| default: |
| PRINTM(MINFO, "Auth mode auto!\n"); |
| auth_mode = MLAN_AUTH_MODE_AUTO; |
| break; |
| } |
| if (MLAN_STATUS_SUCCESS != |
| woal_set_auth_mode(priv, MOAL_IOCTL_WAIT, auth_mode)) |
| ret = -EFAULT; |
| break; |
| case MW_AUTH_WPA_ENABLED: |
| if (MLAN_STATUS_SUCCESS != |
| woal_set_wpa_enable(priv, MOAL_IOCTL_WAIT, |
| mwr->u.param.value)) |
| ret = -EFAULT; |
| break; |
| #define MW_AUTH_WAPI_ENABLED 0x20 |
| case MW_AUTH_WAPI_ENABLED: |
| if (MLAN_STATUS_SUCCESS != |
| woal_set_wapi_enable(priv, MOAL_IOCTL_WAIT, |
| mwr->u.param.value)) |
| ret = -EFAULT; |
| break; |
| case MW_AUTH_WPA_VERSION: |
| /* set WPA_VERSION_DISABLED/VERSION_WPA/VERSION_WP2 */ |
| priv->wpa_version = mwr->u.param.value; |
| break; |
| case MW_AUTH_KEY_MGMT: |
| /* set KEY_MGMT_802_1X/KEY_MGMT_PSK */ |
| priv->key_mgmt = mwr->u.param.value; |
| break; |
| case MW_AUTH_TKIP_COUNTERMEASURES: |
| case MW_AUTH_DROP_UNENCRYPTED: |
| case MW_AUTH_RX_UNENCRYPTED_EAPOL: |
| case MW_AUTH_ROAMING_CONTROL: |
| case MW_AUTH_PRIVACY_INVOKED: |
| break; |
| default: |
| ret = -EOPNOTSUPP; |
| break; |
| } |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief Get current BSSID |
| * |
| * @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 if successful else negative value |
| */ |
| static int |
| woal_priv_get_ap(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| struct mwreq *mwr; |
| t_u8 *data_ptr; |
| int ret = 0; |
| mlan_bss_info bss_info; |
| |
| ENTER(); |
| |
| data_ptr = respbuf + (strlen(CMD_MARVELL) + strlen(PRIV_CMD_GET_AP)); |
| |
| mwr = (struct mwreq *)data_ptr; |
| |
| memset(&bss_info, 0, sizeof(bss_info)); |
| |
| ret = woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info); |
| if (ret != MLAN_STATUS_SUCCESS) |
| return -EFAULT; |
| |
| if (bss_info.media_connected == MTRUE) { |
| memcpy(mwr->u.ap_addr.sa_data, &bss_info.bssid, |
| MLAN_MAC_ADDR_LENGTH); |
| } else { |
| memset(mwr->u.ap_addr.sa_data, 0, MLAN_MAC_ADDR_LENGTH); |
| } |
| mwr->u.ap_addr.sa_family = ARPHRD_ETHER; |
| ret = strlen(CMD_MARVELL) + strlen(PRIV_CMD_GET_AP) + |
| sizeof(struct mwreq); |
| |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief Get power management |
| * |
| * @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 if successful else negative value |
| */ |
| static int |
| woal_priv_get_power(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| struct mwreq *mwr; |
| t_u8 *data_ptr; |
| int ret = 0, ps_mode; |
| |
| ENTER(); |
| |
| data_ptr = respbuf + (strlen(CMD_MARVELL) + strlen(PRIV_CMD_GET_POWER)); |
| |
| mwr = (struct mwreq *)data_ptr; |
| |
| if (MLAN_STATUS_SUCCESS != woal_set_get_power_mgmt(priv, |
| MLAN_ACT_GET, |
| &ps_mode, 0, |
| MOAL_IOCTL_WAIT)) { |
| return -EFAULT; |
| } |
| |
| if (ps_mode) |
| mwr->u.power.disabled = 0; |
| else |
| mwr->u.power.disabled = 1; |
| |
| mwr->u.power.value = 0; |
| ret = strlen(CMD_MARVELL) + strlen(PRIV_CMD_GET_POWER) + |
| sizeof(struct mwreq); |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief Set/Get power save mode |
| * |
| * @param priv A pointer to moal_private structure |
| * @param respbuf A pointer to response buffer |
| * @param respbuflen Available length of response buffer |
| * |
| * @return 0 --success, otherwise fail |
| */ |
| static int |
| woal_priv_set_get_psmode(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| int ret = 0; |
| int data = 0; |
| int user_data_len = 0, header_len = 0; |
| t_u32 action = MLAN_ACT_GET; |
| |
| ENTER(); |
| |
| header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_PSMODE); |
| |
| if (strlen(respbuf) == header_len) { |
| /* GET operation */ |
| user_data_len = 0; |
| action = MLAN_ACT_GET; |
| } else { |
| /* SET operation */ |
| parse_arguments(respbuf + header_len, &data, |
| sizeof(data) / sizeof(int), &user_data_len); |
| action = MLAN_ACT_SET; |
| } |
| |
| if (sizeof(int) * user_data_len > sizeof(data)) { |
| PRINTM(MERROR, "Too many arguments\n"); |
| ret = -EINVAL; |
| goto done; |
| } |
| |
| /* Flip the value */ |
| data = !data; |
| |
| if (MLAN_STATUS_SUCCESS != |
| woal_set_get_power_mgmt(priv, action, &data, 0, MOAL_IOCTL_WAIT)) { |
| ret = -EFAULT; |
| goto done; |
| } |
| |
| if (action == MLAN_ACT_SET) |
| data = !data; |
| |
| memcpy(respbuf, (t_u8 *)&data, sizeof(data)); |
| ret = sizeof(data); |
| |
| done: |
| LEAVE(); |
| return ret; |
| } |
| #endif /* STA_SUPPORT */ |
| |
| /** |
| * @brief Performs warm reset |
| * |
| * @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 if successful else negative value |
| */ |
| static int |
| woal_priv_warmreset(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| int ret = 0; |
| int intf_num; |
| moal_handle *handle = priv->phandle; |
| mlan_ioctl_req *req = NULL; |
| mlan_ds_misc_cfg *misc = NULL; |
| #if defined(WIFI_DIRECT_SUPPORT) |
| #if defined(STA_SUPPORT) && defined(UAP_SUPPORT) |
| #if defined(STA_WEXT) || defined(UAP_WEXT) |
| t_u8 bss_role = MLAN_BSS_ROLE_STA; |
| #endif |
| #endif |
| #endif /* WIFI_DIRECT_SUPPORT && V14_FEATURE */ |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| |
| 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); |
| #if defined(WIFI_DIRECT_SUPPORT) |
| #if defined(STA_SUPPORT) && defined(UAP_SUPPORT) |
| #if defined(STA_WEXT) || defined(UAP_WEXT) |
| if ((handle->priv[intf_num]->bss_type == |
| MLAN_BSS_TYPE_WIFIDIRECT) && |
| (GET_BSS_ROLE(handle->priv[intf_num]) == |
| MLAN_BSS_ROLE_UAP)) { |
| if (MLAN_STATUS_SUCCESS != |
| woal_bss_role_cfg(handle->priv[intf_num], |
| MLAN_ACT_SET, MOAL_IOCTL_WAIT, |
| &bss_role)) { |
| ret = -EFAULT; |
| goto done; |
| } |
| } |
| #endif /* STA_WEXT || UAP_WEXT */ |
| #endif /* STA_SUPPORT && UAP_SUPPORT */ |
| #endif /* WIFI_DIRECT_SUPPORT && V14_FEATURE */ |
| } |
| |
| /* 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; |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| if (status != MLAN_STATUS_PENDING) |
| 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 Set/Get TX power configurations |
| * |
| * @param priv A pointer to moal_private structure |
| * @param respbuf A pointer to response buffer |
| * @param respbuflen Available length of response buffer |
| * |
| * @return 0 --success, otherwise fail |
| */ |
| static int |
| woal_priv_txpowercfg(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| int data[4]; |
| int user_data_len; |
| int ret = 0; |
| mlan_bss_info bss_info; |
| mlan_ds_power_cfg *pcfg = NULL; |
| mlan_ioctl_req *req = NULL; |
| t_u8 *arguments = NULL; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| ENTER(); |
| |
| memset(data, 0, sizeof(data)); |
| memset(&bss_info, 0, sizeof(bss_info)); |
| woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info); |
| |
| if (strlen(respbuf) == |
| (strlen(CMD_MARVELL) + strlen(PRIV_CMD_TXPOWERCFG))) { |
| /* GET operation */ |
| user_data_len = 0; |
| } else { |
| /* SET operation */ |
| arguments = |
| respbuf + strlen(CMD_MARVELL) + |
| strlen(PRIV_CMD_TXPOWERCFG); |
| parse_arguments(arguments, data, ARRAY_SIZE(data), |
| &user_data_len); |
| } |
| |
| 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 { |
| /* SET operation */ |
| req->action = MLAN_ACT_SET; |
| if (sizeof(int) * user_data_len > sizeof(data)) { |
| PRINTM(MERROR, "Too many arguments\n"); |
| ret = -EINVAL; |
| goto done; |
| } |
| switch (user_data_len) { |
| case 1: |
| if (data[0] != 0xFF) |
| ret = -EINVAL; |
| break; |
| case 2: |
| case 4: |
| if (data[0] == 0xFF) { |
| ret = -EINVAL; |
| break; |
| } |
| if (data[1] < bss_info.min_power_level) { |
| PRINTM(MERROR, |
| "The set powercfg rate value %d dBm is out of range (%d dBm-%d dBm)!\n", |
| data[1], (int)bss_info.min_power_level, |
| (int)bss_info.max_power_level); |
| ret = -EINVAL; |
| break; |
| } |
| if (user_data_len == 4) { |
| if (data[1] > data[2]) { |
| PRINTM(MERROR, |
| "Min power should be less than maximum!\n"); |
| ret = -EINVAL; |
| break; |
| } |
| if (data[3] < 0) { |
| PRINTM(MERROR, |
| "Step should not less than 0!\n"); |
| ret = -EINVAL; |
| break; |
| } |
| if (data[2] > bss_info.max_power_level) { |
| PRINTM(MERROR, |
| "The set powercfg rate value %d dBm is out of range (%d dBm-%d dBm)!\n", |
| data[2], |
| (int)bss_info.min_power_level, |
| (int)bss_info.max_power_level); |
| ret = -EINVAL; |
| break; |
| } |
| if (data[3] > data[2] - data[1]) { |
| PRINTM(MERROR, |
| "Step should not greater than power difference!\n"); |
| ret = -EINVAL; |
| break; |
| } |
| } |
| break; |
| default: |
| ret = -EINVAL; |
| break; |
| } |
| if (ret) |
| goto done; |
| pcfg->param.power_ext.len = user_data_len; |
| memcpy((t_u8 *)&pcfg->param.power_ext.power_data, |
| (t_u8 *)data, sizeof(data)); |
| } |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| goto done; |
| } |
| if (!user_data_len) { |
| /* GET operation */ |
| memcpy(respbuf, (t_u8 *)&pcfg->param.power_ext, |
| sizeof(pcfg->param.power_ext)); |
| ret = sizeof(pcfg->param.power_ext); |
| } |
| done: |
| if (status != MLAN_STATUS_PENDING) |
| kfree(req); |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief Set/Get PS configuration parameters |
| * |
| * @param priv A pointer to moal_private structure |
| * @param respbuf A pointer to response buffer |
| * @param respbuflen Available length of response buffer |
| * |
| * @return 0 --success, otherwise fail |
| */ |
| static int |
| woal_priv_pscfg(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| int data[7] = { 0 }, ret = 0; |
| mlan_ds_pm_cfg *pm_cfg = NULL; |
| mlan_ioctl_req *req = NULL; |
| int allowed = 3; |
| int i = 3; |
| int user_data_len = 0, header_len = 0; |
| t_u8 *arguments = NULL, *space_ind = NULL; |
| t_u32 is_negative_1 = MFALSE, is_negative_2 = MFALSE; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| gfp_t flag; |
| |
| 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; |
| } |
| |
| header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_PSCFG); |
| |
| if (strlen(respbuf) == header_len) { |
| /* GET operation */ |
| user_data_len = 0; |
| } else { |
| /* SET operation */ |
| memset((char *)data, 0, sizeof(data)); |
| flag = (in_atomic() || |
| irqs_disabled())? GFP_ATOMIC : GFP_KERNEL; |
| arguments = kzalloc(strlen(respbuf) * sizeof(char), flag); |
| if (arguments == NULL) { |
| ret = -ENOMEM; |
| goto done; |
| } |
| strcpy(arguments, respbuf + header_len); |
| if (*(char *)arguments == '-') { |
| is_negative_1 = MTRUE; |
| arguments += sizeof(char); |
| } |
| space_ind = strstr((char *)arguments, " "); |
| if (space_ind) |
| space_ind = strstr(space_ind + 1, " "); |
| if (space_ind) { |
| if (*(char *)(space_ind + 1) == '-') { |
| is_negative_2 = MTRUE; |
| arguments[space_ind + 1 - arguments] = '\0'; |
| strcat(arguments, space_ind + 2); |
| } |
| } |
| parse_arguments(arguments, data, ARRAY_SIZE(data), |
| &user_data_len); |
| if (is_negative_1 == MTRUE) |
| data[0] = -1; |
| if (is_negative_2 == MTRUE) |
| data[2] = -1; |
| } |
| |
| if (user_data_len && user_data_len > allowed) { |
| PRINTM(MERROR, "Too many arguments\n"); |
| 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; |
| if (user_data_len) { |
| 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 (user_data_len < 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; |
| |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| goto done; |
| } |
| memcpy(data, &pm_cfg->param.ps_cfg, |
| MIN((sizeof(int) * allowed), sizeof(pm_cfg->param.ps_cfg))); |
| memcpy(respbuf, (t_u8 *)data, sizeof(int) * allowed); |
| ret = sizeof(int) * allowed; |
| if (req->action == MLAN_ACT_SET) { |
| pm_cfg = (mlan_ds_pm_cfg *)req->pbuf; |
| pm_cfg->sub_command = MLAN_OID_PM_CFG_IEEE_PS; |
| pm_cfg->param.ps_mode = 1; |
| req->req_id = MLAN_IOCTL_PM_CFG; |
| req->action = MLAN_ACT_SET; |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| } |
| |
| done: |
| if (status != MLAN_STATUS_PENDING) |
| kfree(req); |
| kfree(arguments); |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief Set/Get sleep period |
| * |
| * @param priv A pointer to moal_private structure |
| * @param respbuf A pointer to response buffer |
| * @param respbuflen Available length of response buffer |
| * |
| * @return 0 --success, otherwise fail |
| */ |
| static int |
| woal_priv_sleeppd(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| int ret = 0; |
| mlan_ds_pm_cfg *pm_cfg = NULL; |
| mlan_ioctl_req *req = NULL; |
| int data = 0; |
| int user_data_len = 0, header_len = 0; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| |
| req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_pm_cfg)); |
| if (req == NULL) { |
| ret = -ENOMEM; |
| goto done; |
| } |
| pm_cfg = (mlan_ds_pm_cfg *)req->pbuf; |
| pm_cfg->sub_command = MLAN_OID_PM_CFG_SLEEP_PD; |
| req->req_id = MLAN_IOCTL_PM_CFG; |
| |
| header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_SLEEPPD); |
| |
| if (strlen(respbuf) == header_len) { |
| /* GET operation */ |
| user_data_len = 0; |
| } else { |
| /* SET operation */ |
| parse_arguments(respbuf + header_len, &data, |
| sizeof(data) / sizeof(int), &user_data_len); |
| } |
| |
| if (sizeof(int) * user_data_len > sizeof(data)) { |
| PRINTM(MERROR, "Too many arguments\n"); |
| LEAVE(); |
| return -EINVAL; |
| } |
| |
| if (user_data_len) { |
| if ((data <= MAX_SLEEP_PERIOD && data >= MIN_SLEEP_PERIOD) || |
| (data == 0) |
| || (data == SLEEP_PERIOD_RESERVED_FF) |
| ) { |
| req->action = MLAN_ACT_SET; |
| pm_cfg->param.sleep_period = data; |
| } else { |
| ret = -EINVAL; |
| goto done; |
| } |
| } else |
| req->action = MLAN_ACT_GET; |
| |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| goto done; |
| } |
| if (!user_data_len) { |
| data = pm_cfg->param.sleep_period; |
| memcpy(respbuf, (t_u8 *)&data, sizeof(data)); |
| ret = sizeof(data); |
| } |
| |
| done: |
| if (status != MLAN_STATUS_PENDING) |
| kfree(req); |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief Set/Get Tx control flag |
| * |
| * @param priv A pointer to moal_private structure |
| * @param respbuf A pointer to response buffer |
| * @param respbuflen Available length of response buffer |
| * |
| * @return 0 --success, otherwise fail |
| */ |
| static int |
| woal_priv_txcontrol(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| int ret = 0; |
| mlan_ds_misc_cfg *misc_cfg = NULL; |
| mlan_ioctl_req *req = NULL; |
| int data = 0; |
| int user_data_len = 0, header_len = 0; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| |
| req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); |
| if (req == NULL) { |
| ret = -ENOMEM; |
| goto done; |
| } |
| misc_cfg = (mlan_ds_misc_cfg *)req->pbuf; |
| misc_cfg->sub_command = MLAN_OID_MISC_TXCONTROL; |
| req->req_id = MLAN_IOCTL_MISC_CFG; |
| |
| header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_TXCONTROL); |
| |
| if (strlen(respbuf) == header_len) { |
| /* GET operation */ |
| user_data_len = 0; |
| } else { |
| /* SET operation */ |
| parse_arguments(respbuf + header_len, &data, |
| sizeof(data) / sizeof(int), &user_data_len); |
| } |
| |
| if (sizeof(int) * user_data_len > sizeof(data)) { |
| PRINTM(MERROR, "Too many arguments\n"); |
| LEAVE(); |
| return -EINVAL; |
| } |
| |
| if (user_data_len) { |
| req->action = MLAN_ACT_SET; |
| misc_cfg->param.tx_control = (t_u32)data; |
| } else { |
| req->action = MLAN_ACT_GET; |
| } |
| |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| goto done; |
| } |
| if (!user_data_len) { |
| data = misc_cfg->param.tx_control; |
| memcpy(respbuf, (t_u8 *)&data, sizeof(data)); |
| ret = sizeof(data); |
| } |
| |
| done: |
| if (status != MLAN_STATUS_PENDING) |
| kfree(req); |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief Read/Write adapter registers value |
| * |
| * @param priv A pointer to moal_private structure |
| * @param respbuf A pointer to response buffer |
| * @param respbuflen Available length of response buffer |
| * |
| * @return 0 --success, otherwise fail |
| */ |
| static int |
| woal_priv_regrdwr(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| int data[3]; |
| int ret = 0; |
| mlan_ioctl_req *req = NULL; |
| mlan_ds_reg_mem *reg_mem = NULL; |
| int user_data_len = 0, header_len = 0; |
| t_u8 *arguments = NULL, *space_ind = NULL; |
| t_u32 is_negative_val = MFALSE; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| gfp_t flag; |
| |
| ENTER(); |
| |
| 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_REG_RW; |
| req->req_id = MLAN_IOCTL_REG_MEM; |
| |
| header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_REGRDWR); |
| |
| if (strlen(respbuf) == header_len) { |
| ret = -EINVAL; |
| goto done; |
| } |
| /* SET operation */ |
| memset((char *)data, 0, sizeof(data)); |
| flag = (in_atomic() || irqs_disabled())? GFP_ATOMIC : GFP_KERNEL; |
| arguments = kzalloc(strlen(respbuf) * sizeof(char), flag); |
| if (arguments == NULL) { |
| ret = -ENOMEM; |
| goto done; |
| } |
| strcpy(arguments, respbuf + header_len); |
| space_ind = strstr((char *)arguments, " "); |
| if (space_ind) |
| space_ind = strstr(space_ind + 1, " "); |
| if (space_ind) { |
| if (*(char *)(space_ind + 1) == '-') { |
| is_negative_val = MTRUE; |
| arguments[space_ind + 1 - arguments] = '\0'; |
| strcat(arguments, space_ind + 2); |
| } |
| } |
| parse_arguments(arguments, data, ARRAY_SIZE(data), &user_data_len); |
| if (is_negative_val == MTRUE) |
| data[2] *= -1; |
| |
| if (user_data_len == 2) { |
| req->action = MLAN_ACT_GET; |
| } else if (user_data_len == 3) { |
| req->action = MLAN_ACT_SET; |
| } else { |
| ret = -EINVAL; |
| goto done; |
| } |
| |
| reg_mem->param.reg_rw.type = (t_u32)data[0]; |
| reg_mem->param.reg_rw.offset = (t_u32)data[1]; |
| if (user_data_len == 3) |
| reg_mem->param.reg_rw.value = (t_u32)data[2]; |
| |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| goto done; |
| } |
| |
| if (req->action == MLAN_ACT_GET) { |
| memcpy(respbuf, ®_mem->param.reg_rw, |
| sizeof(reg_mem->param.reg_rw)); |
| ret = sizeof(reg_mem->param.reg_rw); |
| } |
| |
| done: |
| if (status != MLAN_STATUS_PENDING) |
| kfree(req); |
| kfree(arguments); |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief Read the EEPROM contents of the card |
| * |
| * @param priv A pointer to moal_private structure |
| * @param respbuf A pointer to response buffer |
| * @param respbuflen Available length of response buffer |
| * |
| * @return 0 --success, otherwise fail |
| */ |
| static int |
| woal_priv_rdeeprom(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| int data[2]; |
| int ret = 0; |
| mlan_ioctl_req *req = NULL; |
| mlan_ds_reg_mem *reg_mem = NULL; |
| int user_data_len = 0, header_len = 0; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| |
| 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_EEPROM_RD; |
| req->req_id = MLAN_IOCTL_REG_MEM; |
| |
| header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_RDEEPROM); |
| |
| if (strlen(respbuf) == header_len) { |
| ret = -EINVAL; |
| goto done; |
| } |
| /* SET operation */ |
| memset((char *)data, 0, sizeof(data)); |
| parse_arguments(respbuf + header_len, data, ARRAY_SIZE(data), |
| &user_data_len); |
| |
| if (user_data_len == 2) { |
| req->action = MLAN_ACT_GET; |
| } else { |
| ret = -EINVAL; |
| goto done; |
| } |
| |
| reg_mem->param.rd_eeprom.offset = (t_u16)data[0]; |
| reg_mem->param.rd_eeprom.byte_count = (t_u16)data[1]; |
| |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| goto done; |
| } |
| |
| if (req->action == MLAN_ACT_GET) { |
| memcpy(respbuf, ®_mem->param.rd_eeprom, |
| sizeof(reg_mem->param.rd_eeprom)); |
| ret = sizeof(reg_mem->param.rd_eeprom); |
| } |
| |
| done: |
| if (status != MLAN_STATUS_PENDING) |
| kfree(req); |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief Read/Write device memory value |
| * |
| * @param priv A pointer to moal_private structure |
| * @param respbuf A pointer to response buffer |
| * @param respbuflen Available length of response buffer |
| * |
| * @return 0 --success, otherwise fail |
| */ |
| static int |
| woal_priv_memrdwr(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| int data[2]; |
| int ret = 0; |
| mlan_ioctl_req *req = NULL; |
| mlan_ds_reg_mem *reg_mem = NULL; |
| int user_data_len = 0, header_len = 0; |
| t_u8 *arguments = NULL, *space_ind = NULL; |
| t_u32 is_negative_1 = MFALSE, is_negative_2 = MFALSE; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| gfp_t flag; |
| |
| ENTER(); |
| |
| 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; |
| |
| header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_MEMRDWR); |
| |
| if (strlen(respbuf) == header_len) { |
| ret = -EINVAL; |
| goto done; |
| } |
| /* SET operation */ |
| memset((char *)data, 0, sizeof(data)); |
| flag = (in_atomic() || irqs_disabled())? GFP_ATOMIC : GFP_KERNEL; |
| arguments = kzalloc(strlen(respbuf) * sizeof(char), flag); |
| if (arguments == NULL) { |
| ret = -ENOMEM; |
| goto done; |
| } |
| strcpy(arguments, respbuf + header_len); |
| if (*(char *)arguments == '-') { |
| is_negative_1 = MTRUE; |
| arguments += sizeof(char); |
| } |
| space_ind = strstr((char *)arguments, " "); |
| if (space_ind) { |
| if (*(char *)(space_ind + 1) == '-') { |
| is_negative_2 = MTRUE; |
| arguments[space_ind + 1 - arguments] = '\0'; |
| strcat(arguments, space_ind + 2); |
| } |
| } |
| parse_arguments(arguments, data, ARRAY_SIZE(data), &user_data_len); |
| if (is_negative_1 == MTRUE) |
| data[0] *= -1; |
| if (is_negative_2 == MTRUE) |
| data[1] *= -1; |
| |
| if (user_data_len == 1) { |
| PRINTM(MINFO, "MEM_RW: GET\n"); |
| req->action = MLAN_ACT_GET; |
| } else if (user_data_len == 2) { |
| PRINTM(MINFO, "MEM_RW: SET\n"); |
| req->action = MLAN_ACT_SET; |
| } else { |
| ret = -EINVAL; |
| goto done; |
| } |
| |
| reg_mem->param.mem_rw.addr = (t_u32)data[0]; |
| if (user_data_len == 2) |
| reg_mem->param.mem_rw.value = (t_u32)data[1]; |
| |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| goto done; |
| } |
| |
| if (req->action == MLAN_ACT_GET) { |
| memcpy(respbuf, ®_mem->param.mem_rw, |
| sizeof(reg_mem->param.mem_rw)); |
| ret = sizeof(reg_mem->param.mem_rw); |
| } |
| |
| done: |
| if (status != MLAN_STATUS_PENDING) |
| kfree(req); |
| kfree(arguments); |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief Cmd52 read/write register |
| * |
| * @param priv A pointer to moal_private structure |
| * @param respbuf A pointer to response buffer |
| * @param respbuflen Available length of response buffer |
| * |
| * @return MLAN_STATUS_SUCCESS --success, otherwise fail |
| */ |
| static int |
| woal_priv_sdcmd52rw(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| t_u8 rw = 0, func, data = 0; |
| int buf[3], reg, ret = MLAN_STATUS_SUCCESS; |
| int user_data_len = 0, header_len = 0; |
| |
| ENTER(); |
| |
| header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_SDCMD52RW); |
| memset((t_u8 *)buf, 0, sizeof(buf)); |
| |
| if (strlen(respbuf) == header_len) { |
| PRINTM(MERROR, "Invalid number of arguments\n"); |
| ret = -EINVAL; |
| goto done; |
| } |
| parse_arguments(respbuf + header_len, buf, ARRAY_SIZE(buf), |
| &user_data_len); |
| |
| if (user_data_len < 2 || user_data_len > 3) { |
| PRINTM(MERROR, "Invalid number of arguments\n"); |
| ret = -EINVAL; |
| 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 (user_data_len == 2) { |
| rw = 0; /* CMD52 read */ |
| PRINTM(MINFO, "Cmd52 read, func=%d, reg=0x%08X\n", func, reg); |
| } |
| if (user_data_len == 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; |
| } |
| } |
| |
| /* Action = GET */ |
| buf[0] = data; |
| |
| memcpy(respbuf, &buf, sizeof(int)); |
| ret = sizeof(int); |
| |
| done: |
| LEAVE(); |
| return ret; |
| } |
| |
| #ifdef STA_SUPPORT |
| /** |
| * @brief arpfilter ioctl function |
| * |
| * @param priv A pointer to moal_private structure |
| * @param respbuf A pointer to response buffer |
| * @param respbuflen Available length of response buffer |
| * |
| * @return 0 --success, otherwise fail |
| */ |
| static int |
| woal_priv_arpfilter(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| int ret = 0; |
| mlan_ds_misc_cfg *misc = NULL; |
| mlan_ioctl_req *req = NULL; |
| t_u8 *data_ptr = NULL; |
| t_u32 buf_len = 0; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| |
| req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); |
| if (req == NULL) { |
| ret = -ENOMEM; |
| goto done; |
| } |
| 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; |
| |
| data_ptr = respbuf + (strlen(CMD_MARVELL) + strlen(PRIV_CMD_ARPFILTER)); |
| buf_len = *((t_u16 *)data_ptr); |
| misc->param.gen_ie.len = buf_len; |
| memcpy((void *)(misc->param.gen_ie.ie_data), data_ptr + sizeof(buf_len), |
| buf_len); |
| |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| goto done; |
| } |
| done: |
| if (status != MLAN_STATUS_PENDING) |
| kfree(req); |
| LEAVE(); |
| return ret; |
| } |
| #endif /* STA_SUPPORT */ |
| |
| /** |
| * @brief Set/Get hotspot mode configurations |
| * |
| * @param priv A pointer to moal_private structure |
| * @param respbuf A pointer to response buffer |
| * @param respbuflen Available length of response buffer |
| * |
| * @return 0 --success, otherwise fail |
| */ |
| int |
| woal_priv_hotspotcfg(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| int ret = 0; |
| mlan_ioctl_req *req = NULL; |
| mlan_ds_misc_cfg *cfg = NULL; |
| int data = 0; |
| int user_data_len = 0, header_len = 0; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| |
| header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_HOTSPOTCFG); |
| if (strlen(respbuf) == header_len) { |
| /* GET operation */ |
| user_data_len = 0; |
| } else { |
| /* SET operation */ |
| parse_arguments(respbuf + header_len, &data, 1, &user_data_len); |
| } |
| if (user_data_len >= 2) { |
| PRINTM(MERROR, "Too many arguments\n"); |
| ret = -EINVAL; |
| goto done; |
| } else { |
| 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 (user_data_len == 0) { |
| req->action = MLAN_ACT_GET; |
| } else { |
| cfg->param.hotspot_cfg = data; |
| req->action = MLAN_ACT_SET; |
| } |
| } |
| cfg->sub_command = MLAN_OID_MISC_HOTSPOT_CFG; |
| req->req_id = MLAN_IOCTL_MISC_CFG; |
| |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| goto done; |
| } |
| data = cfg->param.hotspot_cfg; |
| memcpy(respbuf, (t_u8 *)&data, sizeof(data)); |
| ret = sizeof(data); |
| |
| done: |
| if (status != MLAN_STATUS_PENDING) |
| kfree(req); |
| LEAVE(); |
| return ret; |
| |
| } |
| |
| extern int woal_peer_mgmt_frame_ioctl(t_u16 mask); |
| |
| |
| /** |
| * @brief Set/Get Mgmt Frame passthru mask |
| * |
| * @param priv A pointer to moal_private structure |
| * @param respbuf A pointer to response buffer |
| * @param respbuflen Available length of response buffer |
| * |
| * @return 0 --success, otherwise fail |
| */ |
| int |
| woal_priv_mgmt_frame_passthru_ctrl(moal_private *priv, t_u8 *respbuf, |
| t_u32 respbuflen) |
| { |
| int ret = 0; |
| int data = 0; |
| int user_data_len = 0, header_len = 0; |
| mlan_ioctl_req *req = NULL; |
| mlan_ds_misc_cfg *mgmt_cfg = NULL; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_MGMT_FRAME_CTRL); |
| if (strlen(respbuf) == header_len) { |
| /* GET operation */ |
| user_data_len = 0; |
| } else { |
| /* SET operation */ |
| parse_arguments(respbuf + header_len, &data, 1, &user_data_len); |
| } |
| |
| if (user_data_len >= 2) { |
| PRINTM(MERROR, "Too many arguments\n"); |
| ret = -EINVAL; |
| goto done; |
| } else { |
| req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); |
| if (req == NULL) { |
| ret = -ENOMEM; |
| goto done; |
| } |
| 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 (user_data_len == 0) { /* Get */ |
| req->action = MLAN_ACT_GET; |
| } else { /* Set */ |
| mgmt_cfg->param.mgmt_subtype_mask = data; |
| req->action = MLAN_ACT_SET; |
| //Set the proc variable 'enable' |
| woal_peer_mgmt_frame_ioctl((t_u16) mgmt_cfg->param.mgmt_subtype_mask); |
| } |
| } |
| |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| goto done; |
| } |
| |
| data = mgmt_cfg->param.mgmt_subtype_mask; |
| memcpy(respbuf, (t_u8 *)&data, sizeof(data)); |
| ret = sizeof(data); |
| |
| done: |
| if (status != MLAN_STATUS_PENDING) |
| kfree(req); |
| LEAVE(); |
| return ret; |
| |
| } |
| |
| /** |
| * @brief Private IOCTL entry to send an ADDTS TSPEC |
| * |
| * Receive a ADDTS command from the application. The command structure |
| * contains a TSPEC and timeout in milliseconds. The timeout is performed |
| * in the firmware after the ADDTS command frame is sent. |
| * |
| * The TSPEC is received in the API as an opaque block. The firmware will |
| * send the entire data block, including the bytes after the TSPEC. This |
| * is done to allow extra IEs to be packaged with the TSPEC in the ADDTS |
| * action frame. |
| * |
| * The IOCTL structure contains two return fields: |
| * - The firmware command result, which indicates failure and timeouts |
| * - The IEEE Status code which contains the corresponding value from |
| * any ADDTS response frame received. |
| * |
| * In addition, the opaque TSPEC data block passed in is replaced with the |
| * TSPEC received in the ADDTS response frame. In case of failure, the |
| * AP may modify the TSPEC on return and in the case of success, the |
| * medium time is returned as calculated by the AP. Along with the TSPEC, |
| * any IEs that are sent in the ADDTS response are also returned and can be |
| * parsed using the IOCTL length as an indicator of extra elements. |
| * |
| * The return value to the application layer indicates a driver execution |
| * success or failure. A successful return could still indicate a firmware |
| * failure or AP negotiation failure via the commandResult field copied |
| * back to the application. |
| * |
| * @param priv Pointer to the mlan_private driver data struct |
| * @param respbuf A pointer to response buffer |
| * @param respbuflen Available length of response buffer |
| * |
| * @return Number of bytes written if successful else negative value |
| */ |
| static int |
| woal_priv_wmm_addts_req_ioctl(moal_private *priv, t_u8 *respbuf, |
| t_u32 respbuflen) |
| { |
| mlan_ioctl_req *req = NULL; |
| mlan_ds_wmm_cfg *cfg = NULL; |
| wlan_ioctl_wmm_addts_req_t addts_ioctl; |
| int ret = 0, header_len = 0, copy_len = sizeof(addts_ioctl); |
| t_u8 *data_ptr; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| |
| header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_ADDTS); |
| data_ptr = respbuf + header_len; |
| 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)); |
| |
| memcpy((t_u8 *)&addts_ioctl, data_ptr, sizeof(addts_ioctl)); |
| |
| cfg->param.addts.timeout = addts_ioctl.timeout_ms; |
| cfg->param.addts.ie_data_len = addts_ioctl.ie_data_len; |
| |
| memcpy(cfg->param.addts.ie_data, |
| addts_ioctl.ie_data, cfg->param.addts.ie_data_len); |
| |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| goto done; |
| } |
| addts_ioctl.cmd_result = cfg->param.addts.result; |
| addts_ioctl.ieee_status_code = (t_u8)cfg->param.addts.status_code; |
| addts_ioctl.ie_data_len = cfg->param.addts.ie_data_len; |
| |
| memcpy(addts_ioctl.ie_data, |
| cfg->param.addts.ie_data, cfg->param.addts.ie_data_len); |
| |
| copy_len = (sizeof(addts_ioctl) |
| - sizeof(addts_ioctl.ie_data) |
| + cfg->param.addts.ie_data_len); |
| |
| memcpy(respbuf, (t_u8 *)&addts_ioctl, copy_len); |
| ret = copy_len; |
| |
| done: |
| if (status != MLAN_STATUS_PENDING) |
| kfree(req); |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief Private IOCTL entry to send a DELTS TSPEC |
| * |
| * Receive a DELTS command from the application. The command structure |
| * contains a TSPEC and reason code along with space for a command result |
| * to be returned. The information is packaged is sent to the wlan_cmd.c |
| * firmware command prep and send routines for execution in the firmware. |
| * |
| * The reason code is not used for WMM implementations but is indicated in |
| * the 802.11e specification. |
| * |
| * The return value to the application layer indicates a driver execution |
| * success or failure. A successful return could still indicate a firmware |
| * failure via the cmd_result field copied back to the application. |
| * |
| * @param priv Pointer to the mlan_private driver data struct |
| * @param respbuf A pointer to response buffer |
| * @param respbuflen Available length of response buffer |
| * |
| * @return Number of bytes written if successful else negative value |
| */ |
| static int |
| woal_priv_wmm_delts_req_ioctl(moal_private *priv, t_u8 *respbuf, |
| t_u32 respbuflen) |
| { |
| mlan_ioctl_req *req = NULL; |
| mlan_ds_wmm_cfg *cfg = NULL; |
| wlan_ioctl_wmm_delts_req_t delts_ioctl; |
| int ret = 0, header_len = 0, copy_len = 0; |
| t_u8 *data_ptr; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| |
| header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_DELTS); |
| data_ptr = respbuf + header_len; |
| 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 (strlen(respbuf) > header_len) { |
| copy_len = MIN(strlen(data_ptr), sizeof(delts_ioctl)); |
| memcpy((t_u8 *)&delts_ioctl, data_ptr, copy_len); |
| |
| 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); |
| |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| goto done; |
| } |
| |
| /* Return the firmware command result back to the application |
| layer */ |
| delts_ioctl.cmd_result = cfg->param.delts.result; |
| copy_len = sizeof(delts_ioctl); |
| memcpy(respbuf, (t_u8 *)&delts_ioctl, copy_len); |
| ret = copy_len; |
| } |
| |
| done: |
| if (status != MLAN_STATUS_PENDING) |
| kfree(req); |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief Private IOCTL entry to get/set a specified AC Queue's parameters |
| * |
| * Receive a AC Queue configuration command which is used to get, set, or |
| * default the parameters associated with a specific WMM AC Queue. |
| * |
| * @param priv A pointer to moal_private structure |
| * @param respbuf A pointer to response buffer |
| * @param respbuflen Available length of response buffer |
| * |
| * @return 0 --success, otherwise fail |
| */ |
| static int |
| woal_priv_qconfig(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| 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; |
| t_u8 *data_ptr; |
| int ret = 0; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| |
| req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_wmm_cfg)); |
| if (req == NULL) { |
| ret = -ENOMEM; |
| goto done; |
| } |
| |
| req->req_id = MLAN_IOCTL_WMM_CFG; |
| pwmm = (mlan_ds_wmm_cfg *)req->pbuf; |
| pwmm->sub_command = MLAN_OID_WMM_CFG_QUEUE_CONFIG; |
| |
| memset(&qcfg_ioctl, 0x00, sizeof(qcfg_ioctl)); |
| pqcfg = (mlan_ds_wmm_queue_config *)&pwmm->param.q_cfg; |
| data_ptr = respbuf + (strlen(CMD_MARVELL) + strlen(PRIV_CMD_QCONFIG)); |
| |
| memcpy((t_u8 *)&qcfg_ioctl, data_ptr, sizeof(qcfg_ioctl)); |
| pqcfg->action = qcfg_ioctl.action; |
| pqcfg->access_category = qcfg_ioctl.access_category; |
| pqcfg->msdu_lifetime_expiry = qcfg_ioctl.msdu_lifetime_expiry; |
| |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| goto done; |
| } |
| memset(&qcfg_ioctl, 0x00, sizeof(qcfg_ioctl)); |
| qcfg_ioctl.action = pqcfg->action; |
| qcfg_ioctl.access_category = pqcfg->access_category; |
| qcfg_ioctl.msdu_lifetime_expiry = pqcfg->msdu_lifetime_expiry; |
| memcpy(data_ptr, (t_u8 *)&qcfg_ioctl, sizeof(qcfg_ioctl)); |
| ret = strlen(CMD_MARVELL) + strlen(PRIV_CMD_QCONFIG) + |
| sizeof(qcfg_ioctl); |
| done: |
| if (status != MLAN_STATUS_PENDING) |
| kfree(req); |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief Private IOCTL entry to get the status of the WMM queues |
| * |
| * Return the following information for each WMM AC: |
| * - WMM IE Acm Required |
| * - Firmware Flow Required |
| * - Firmware Flow Established |
| * - Firmware Queue Enabled |
| * - Firmware Delivery Enabled |
| * - Firmware Trigger Enabled |
| * |
| * @param priv Pointer to the moal_private driver data struct |
| * @param respbuf A pointer to response buffer |
| * @param respbuflen Available length of response buffer |
| * |
| * @return Number of bytes written if successful else negative value |
| */ |
| static int |
| woal_priv_wmm_queue_status_ioctl(moal_private *priv, t_u8 *respbuf, |
| t_u32 respbuflen) |
| { |
| mlan_ioctl_req *req = NULL; |
| mlan_ds_wmm_cfg *pwmm = NULL; |
| wlan_ioctl_wmm_queue_status_t qstatus_ioctl; |
| int ret = 0, header_len = 0; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| |
| header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_QSTATUS); |
| 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 (strlen(respbuf) == header_len) { |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| goto done; |
| } |
| |
| memset(&qstatus_ioctl, 0x00, sizeof(qstatus_ioctl)); |
| memcpy((void *)&qstatus_ioctl, (void *)&pwmm->param.q_status, |
| sizeof(qstatus_ioctl)); |
| memcpy(respbuf, (t_u8 *)&qstatus_ioctl, sizeof(qstatus_ioctl)); |
| ret = sizeof(qstatus_ioctl); |
| } |
| |
| done: |
| if (status != MLAN_STATUS_PENDING) |
| kfree(req); |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief Private IOCTL entry to get the status of the WMM Traffic Streams |
| * |
| * @param priv Pointer to the moal_private driver data struct |
| * @param respbuf A pointer to response buffer |
| * @param respbuflen Available length of response buffer |
| * |
| * @return Number of bytes written if successful else negative value |
| */ |
| static int |
| woal_priv_wmm_ts_status_ioctl(moal_private *priv, t_u8 *respbuf, |
| t_u32 respbuflen) |
| { |
| mlan_ioctl_req *req = NULL; |
| mlan_ds_wmm_cfg *pwmm = NULL; |
| wlan_ioctl_wmm_ts_status_t ts_status_ioctl; |
| int ret = 0, header_len = 0; |
| t_u8 *data_ptr; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| |
| header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_TS_STATUS); |
| data_ptr = respbuf + header_len; |
| 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)); |
| |
| memcpy((t_u8 *)&ts_status_ioctl, data_ptr, sizeof(ts_status_ioctl)); |
| |
| memset(&pwmm->param.ts_status, 0x00, sizeof(ts_status_ioctl)); |
| pwmm->param.ts_status.tid = ts_status_ioctl.tid; |
| |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| goto done; |
| } |
| |
| memset(&ts_status_ioctl, 0x00, sizeof(ts_status_ioctl)); |
| memcpy((void *)&ts_status_ioctl, (void *)&pwmm->param.ts_status, |
| sizeof(ts_status_ioctl)); |
| memcpy(respbuf, (t_u8 *)&ts_status_ioctl, sizeof(ts_status_ioctl)); |
| ret = sizeof(ts_status_ioctl); |
| |
| done: |
| if (status != MLAN_STATUS_PENDING) |
| kfree(req); |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief Set/Get MAC control |
| * |
| * @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_macctrl(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| int data = 0; |
| mlan_ioctl_req *req = NULL; |
| mlan_ds_misc_cfg *cfg = NULL; |
| int ret = 0; |
| int user_data_len = 0, header_len = 0; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| |
| header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_MAC_CTRL); |
| if (strlen(respbuf) == header_len) { |
| /* GET operation */ |
| user_data_len = 0; |
| } else { |
| /* SET operation */ |
| parse_arguments(respbuf + header_len, &data, 1, &user_data_len); |
| } |
| |
| if (user_data_len > 1) { |
| PRINTM(MERROR, "Invalid number of arguments\n"); |
| ret = -EINVAL; |
| goto done; |
| } |
| req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); |
| if (req == NULL) { |
| ret = -ENOMEM; |
| goto done; |
| } |
| |
| cfg = (mlan_ds_misc_cfg *)req->pbuf; |
| cfg->sub_command = MLAN_OID_MISC_MAC_CONTROL; |
| req->req_id = MLAN_IOCTL_MISC_CFG; |
| |
| if (user_data_len == 0) |
| req->action = MLAN_ACT_GET; |
| else { |
| cfg->param.mac_ctrl = (t_u32)data; |
| req->action = MLAN_ACT_SET; |
| } |
| |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| goto done; |
| } |
| |
| memcpy(respbuf, (t_u8 *)&cfg->param.mac_ctrl, sizeof(data)); |
| ret = sizeof(data); |
| |
| done: |
| if (status != MLAN_STATUS_PENDING) |
| kfree(req); |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief Get connection status |
| * |
| * @param priv A pointer to moal_private structure |
| * @param respbuf A pointer to response buffer |
| * @param respbuflen Available length of response buffer |
| * |
| * @return 0 --success, otherwise fail |
| */ |
| int |
| woal_priv_getwap(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| int ret = 0; |
| #ifdef STA_SUPPORT |
| mlan_bss_info bss_info; |
| #endif |
| |
| ENTER(); |
| |
| #ifdef STA_SUPPORT |
| if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) { |
| memset(&bss_info, 0, sizeof(bss_info)); |
| |
| woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info); |
| |
| if (bss_info.media_connected == MTRUE) { |
| memcpy(respbuf, (t_u8 *)&bss_info.bssid, |
| MLAN_MAC_ADDR_LENGTH); |
| } else { |
| memset(respbuf, 0, MLAN_MAC_ADDR_LENGTH); |
| } |
| } |
| #endif |
| #ifdef UAP_SUPPORT |
| if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) { |
| if (priv->bss_started) { |
| memcpy(respbuf, priv->current_addr, |
| MLAN_MAC_ADDR_LENGTH); |
| } else { |
| memset(respbuf, 0, MLAN_MAC_ADDR_LENGTH); |
| } |
| } |
| #endif |
| ret = MLAN_MAC_ADDR_LENGTH; |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief Set/Get Region Code |
| * |
| * @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_region_code(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| int data = 0; |
| mlan_ioctl_req *req = NULL; |
| mlan_ds_misc_cfg *cfg = NULL; |
| int ret = 0; |
| int user_data_len = 0, header_len = 0; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| |
| header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_REGION_CODE); |
| if (strlen(respbuf) == header_len) { |
| /* GET operation */ |
| user_data_len = 0; |
| } else { |
| /* SET operation */ |
| parse_arguments(respbuf + header_len, &data, 1, &user_data_len); |
| } |
| |
| if (user_data_len > 1) { |
| PRINTM(MERROR, "Invalid number of arguments\n"); |
| ret = -EINVAL; |
| goto done; |
| } |
| req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); |
| if (req == NULL) { |
| ret = -ENOMEM; |
| goto done; |
| } |
| |
| cfg = (mlan_ds_misc_cfg *)req->pbuf; |
| cfg->sub_command = MLAN_OID_MISC_REGION; |
| req->req_id = MLAN_IOCTL_MISC_CFG; |
| |
| if (user_data_len == 0) |
| req->action = MLAN_ACT_GET; |
| else { |
| cfg->param.region_code = (t_u32)data; |
| req->action = MLAN_ACT_SET; |
| } |
| |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| goto done; |
| } |
| |
| memcpy(respbuf, (t_u8 *)&cfg->param.region_code, sizeof(data)); |
| ret = sizeof(data); |
| |
| done: |
| if (status != MLAN_STATUS_PENDING) |
| kfree(req); |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief Set/Get channel time and buffer weight |
| * |
| * @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_multi_chan_config(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| mlan_ioctl_req *req = NULL; |
| mlan_ds_misc_cfg *cfg = NULL; |
| t_u8 *data_ptr; |
| int ret = 0; |
| int user_data_len = 0, header_len = 0; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| |
| data_ptr = |
| respbuf + strlen(CMD_MARVELL) + strlen(PRIV_CMD_MULTI_CHAN_CFG); |
| header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_MULTI_CHAN_CFG); |
| if (strlen(respbuf) == header_len) { |
| /* GET operation */ |
| user_data_len = 0; |
| } else { |
| /* SET operation */ |
| user_data_len = sizeof(mlan_ds_multi_chan_cfg); |
| } |
| |
| 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_MULTI_CHAN_CFG; |
| req->req_id = MLAN_IOCTL_MISC_CFG; |
| |
| if (user_data_len == 0) { |
| req->action = MLAN_ACT_GET; |
| } else { |
| req->action = MLAN_ACT_SET; |
| memcpy(&cfg->param.multi_chan_cfg, data_ptr, |
| sizeof(mlan_ds_multi_chan_cfg)); |
| } |
| |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| goto done; |
| } |
| |
| memcpy(respbuf, (mlan_ds_multi_chan_cfg *)&cfg->param.multi_chan_cfg, |
| req->buf_len); |
| ret = req->buf_len; |
| |
| done: |
| if (status != MLAN_STATUS_PENDING) |
| kfree(req); |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief Set/Get multi_channel policy 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_priv_multi_chan_policy(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| int ret = 0; |
| int user_data_len = 0, header_len = 0; |
| int data = 0; |
| t_u16 enable; |
| t_u8 action; |
| |
| ENTER(); |
| |
| header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_MULTI_CHAN_POLICY); |
| if (strlen(respbuf) == header_len) { |
| /* GET operation */ |
| user_data_len = 0; |
| } else { |
| /* SET operation */ |
| parse_arguments(respbuf + header_len, &data, 1, &user_data_len); |
| } |
| |
| if (user_data_len > 1) { |
| PRINTM(MERROR, "Invalid number of arguments\n"); |
| ret = -EINVAL; |
| goto done; |
| } |
| |
| if (user_data_len == 0) { |
| action = MLAN_ACT_GET; |
| } else { |
| action = MLAN_ACT_SET; |
| enable = (t_u16)data; |
| } |
| |
| if (MLAN_STATUS_SUCCESS != |
| woal_mc_policy_cfg(priv, &enable, MOAL_IOCTL_WAIT, action)) { |
| ret = -EFAULT; |
| goto done; |
| } |
| memcpy(respbuf, &enable, sizeof(t_u16)); |
| ret = sizeof(t_u16); |
| |
| done: |
| LEAVE(); |
| return ret; |
| } |
| |
| #ifdef RX_PACKET_COALESCE |
| /** |
| * @brief Set/Get RX packet coalesceing 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_priv_rx_pkt_coalesce_cfg(moal_private *priv, t_u8 *respbuf, |
| t_u32 respbuflen) |
| { |
| int ret = 0; |
| t_u32 data[2]; |
| int user_data_len = 0, header_len = 0; |
| mlan_ds_misc_cfg *cfg = NULL; |
| t_u8 *data_ptr; |
| mlan_ioctl_req *req = NULL; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| |
| data_ptr = respbuf + strlen(CMD_MARVELL) + strlen(PRIV_CMD_RX_COAL_CFG); |
| header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_RX_COAL_CFG); |
| if (strlen(respbuf) == header_len) { |
| /* GET operation */ |
| user_data_len = 0; |
| } else { |
| /* SET operation */ |
| parse_arguments(respbuf + header_len, data, ARRAY_SIZE(data), |
| &user_data_len); |
| } |
| |
| if (sizeof(int) * user_data_len > sizeof(data)) { |
| PRINTM(MERROR, "Too many arguments\n"); |
| ret = -EINVAL; |
| goto done; |
| } |
| |
| if ((user_data_len != 0) && (user_data_len != 2)) { |
| PRINTM(MERROR, "Invalid arguments\n"); |
| ret = -EINVAL; |
| goto done; |
| } |
| |
| req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); |
| if (req == NULL) { |
| ret = -ENOMEM; |
| goto done; |
| } |
| |
| cfg = (mlan_ds_misc_cfg *)req->pbuf; |
| cfg->sub_command = MLAN_OID_MISC_RX_PACKET_COALESCE; |
| req->req_id = MLAN_IOCTL_MISC_CFG; |
| |
| if (user_data_len == 0) { |
| req->action = MLAN_ACT_GET; |
| } else { |
| req->action = MLAN_ACT_SET; |
| cfg->param.rx_coalesce.packet_threshold = data[0]; |
| cfg->param.rx_coalesce.delay = data[1]; |
| } |
| |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| goto done; |
| } |
| |
| memcpy(respbuf, |
| (mlan_ds_misc_rx_packet_coalesce *)&cfg->param.rx_coalesce, |
| req->buf_len); |
| ret = req->buf_len; |
| |
| done: |
| LEAVE(); |
| return ret; |
| } |
| #endif |
| /** |
| * @brief Set/Get FW side mac address |
| * |
| * @param priv A pointer to moal_private structure |
| * @param respbuf A pointer to response buffer |
| * @param respbuflen Available length of response buffer |
| * |
| * @return 0 --success, otherwise fail |
| */ |
| int |
| woal_priv_fwmacaddr(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| t_u8 data[ETH_ALEN]; |
| int ret = 0; |
| int header_len = 0; |
| mlan_ioctl_req *req = NULL; |
| mlan_ds_bss *bss = NULL; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| |
| header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_FWMACADDR); |
| |
| /* Allocate an IOCTL request buffer */ |
| req = (mlan_ioctl_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_MAC_ADDR; |
| req->req_id = MLAN_IOCTL_BSS; |
| |
| if (strlen(respbuf) == header_len) { |
| /* GET operation */ |
| req->action = MLAN_ACT_GET; |
| } else { |
| /* SET operation */ |
| req->action = MLAN_ACT_SET; |
| memset(data, 0, sizeof(data)); |
| woal_mac2u8(data, respbuf + header_len); |
| memcpy(bss->param.mac_addr, data, ETH_ALEN); |
| } |
| |
| /* Send IOCTL request to MLAN */ |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| goto done; |
| } |
| |
| memcpy(respbuf, bss->param.mac_addr, sizeof(data)); |
| ret = sizeof(data); |
| HEXDUMP("FW MAC Addr:", respbuf, ETH_ALEN); |
| |
| done: |
| if (status != MLAN_STATUS_PENDING) |
| kfree(req); |
| LEAVE(); |
| return ret; |
| } |
| |
| #if defined(WIFI_DIRECT_SUPPORT) |
| #ifdef STA_CFG80211 |
| #if LINUX_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION |
| /** |
| * @brief Set offchannel |
| * |
| * @param priv A pointer to moal_private structure |
| * @param respbuf A pointer to response buffer |
| * @param respbuflen Available length of response buffer |
| * |
| * @return 0 --success, otherwise fail |
| */ |
| int |
| woal_priv_offchannel(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| int data[3]; |
| int ret = 0; |
| t_u8 status = 1; |
| int user_data_len = 0, header_len = 0; |
| |
| ENTER(); |
| |
| memset(data, 0, sizeof(data)); |
| |
| header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_OFFCHANNEL); |
| |
| if (header_len == strlen(respbuf)) { |
| /* Query current remain on channel status */ |
| if (priv->phandle->remain_on_channel) |
| ret = sprintf(respbuf, |
| "There is pending remain on channel from bss %d\n", |
| priv->phandle->remain_bss_index) + 1; |
| else |
| ret = sprintf(respbuf, |
| "There is no pending remain on channel\n") |
| + 1; |
| goto done; |
| } else |
| parse_arguments(respbuf + header_len, data, ARRAY_SIZE(data), |
| &user_data_len); |
| |
| if (sizeof(int) * user_data_len > sizeof(data)) { |
| PRINTM(MERROR, "Too many arguments\n"); |
| ret = -EINVAL; |
| goto done; |
| } |
| |
| if (user_data_len >= 1) { |
| if ((data[0] != 0) && (data[0] != 1)) { |
| PRINTM(MERROR, "action (%d) must be either 0 or 1\n", |
| data[0]); |
| ret = -EINVAL; |
| goto done; |
| } |
| } |
| if (user_data_len == 2) { |
| if (data[0] == 1) { |
| PRINTM(MERROR, |
| "channel and duration must both the mentioned\n"); |
| ret = -EINVAL; |
| goto done; |
| } else { |
| PRINTM(MWARN, |
| "extra arguments are ignored since action is 'cancel'\n"); |
| } |
| } |
| if (user_data_len == 3) { |
| if (data[0] == 1) { |
| if (data[1] < 0) { |
| PRINTM(MERROR, "channel cannot be negative\n"); |
| ret = -EINVAL; |
| goto done; |
| } |
| if (data[2] < 0) { |
| PRINTM(MERROR, "duration cannot be negative\n"); |
| ret = -EINVAL; |
| goto done; |
| } |
| } |
| } |
| |
| if (data[0] == 0) { |
| if (!priv->phandle->remain_on_channel) { |
| ret = sprintf(respbuf, |
| "There is no pending remain on channel to be canceled\n") |
| + 1; |
| goto done; |
| } |
| if (woal_cfg80211_remain_on_channel_cfg |
| (priv, MOAL_IOCTL_WAIT, MTRUE, &status, NULL, 0, 0)) { |
| PRINTM(MERROR, "remain_on_channel: Failed to cancel\n"); |
| ret = -EFAULT; |
| goto done; |
| } |
| if (status == MLAN_STATUS_SUCCESS) |
| priv->phandle->remain_on_channel = MFALSE; |
| } else if (data[0] == 1) { |
| if (woal_cfg80211_remain_on_channel_cfg |
| (priv, MOAL_IOCTL_WAIT, MFALSE, &status, |
| ieee80211_get_channel(priv->wdev->wiphy, |
| ieee80211_channel_to_frequency(data |
| [1] |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) || defined(COMPAT_WIRELESS) |
| , |
| (data |
| [1] |
| <= |
| 14 ? |
| IEEE80211_BAND_2GHZ |
| : |
| IEEE80211_BAND_5GHZ) |
| #endif |
| )), 0, (t_u32)data[2])) { |
| PRINTM(MERROR, "remain_on_channel: Failed to start\n"); |
| ret = -EFAULT; |
| goto done; |
| } |
| if (status == MLAN_STATUS_SUCCESS) { |
| priv->phandle->remain_on_channel = MTRUE; |
| priv->phandle->remain_bss_index = priv->bss_index; |
| } |
| } |
| |
| if (status != MLAN_STATUS_SUCCESS) |
| ret = -EFAULT; |
| else |
| ret = sprintf(respbuf, "OK\n") + 1; |
| |
| done: |
| LEAVE(); |
| return ret; |
| } |
| #endif |
| #endif |
| #endif |
| |
| /** |
| * @brief Get extended 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_priv_get_driver_verext(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| int data = 0; |
| mlan_ds_get_info *info = NULL; |
| mlan_ioctl_req *req = NULL; |
| int ret = 0; |
| int copy_size = 0; |
| int user_data_len = 0, header_len = 0; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| |
| req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_get_info)); |
| if (req == NULL) { |
| ret = ENOMEM; |
| goto done; |
| } |
| |
| info = (mlan_ds_get_info *)req->pbuf; |
| info->sub_command = MLAN_OID_GET_VER_EXT; |
| req->req_id = MLAN_IOCTL_GET_INFO; |
| req->action = MLAN_ACT_GET; |
| |
| header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_VEREXT); |
| |
| if (strlen(respbuf) == header_len) { |
| /* GET operation */ |
| user_data_len = 0; |
| } else { |
| /* SET operation */ |
| parse_arguments(respbuf + header_len, &data, 1, &user_data_len); |
| } |
| |
| if (user_data_len > 1) { |
| PRINTM(MERROR, "Too many arguments\n"); |
| ret = -EINVAL; |
| goto done; |
| } |
| info->param.ver_ext.version_str_sel = data; |
| if (((t_s32)(info->param.ver_ext.version_str_sel)) < 0) { |
| PRINTM(MERROR, "Invalid arguments!\n"); |
| ret = -EINVAL; |
| goto done; |
| } |
| |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| goto done; |
| } |
| |
| /* |
| * Set the amount to copy back to the application as the minimum of the |
| * available assoc resp data or the buffer provided by the application |
| */ |
| copy_size = MIN(strlen(info->param.ver_ext.version_str), respbuflen); |
| memcpy(respbuf, info->param.ver_ext.version_str, copy_size); |
| ret = copy_size; |
| PRINTM(MINFO, "MOAL EXTENDED VERSION: %s\n", |
| info->param.ver_ext.version_str); |
| |
| done: |
| if (status != MLAN_STATUS_PENDING) |
| kfree(req); |
| LEAVE(); |
| return ret; |
| } |
| |
| #if defined(STA_SUPPORT) |
| /** |
| * @brief SET/Get radio |
| * |
| * @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. |
| */ |
| static int |
| woal_priv_radio_ctrl(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| int ret = 0, option = 0; |
| int user_data_len = 0, header_len = 0; |
| mlan_bss_info bss_info; |
| |
| ENTER(); |
| |
| memset(&bss_info, 0, sizeof(bss_info)); |
| |
| header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_RADIO_CTRL); |
| |
| if (strlen(respbuf) == header_len) { |
| /* GET operation */ |
| user_data_len = 0; |
| } else { |
| /* SET operation */ |
| parse_arguments(respbuf + header_len, &option, 1, |
| &user_data_len); |
| } |
| |
| if (user_data_len > 1) { |
| PRINTM(MERROR, "Too many arguments\n"); |
| ret = -EINVAL; |
| goto done; |
| } |
| if (user_data_len == 1) { |
| /* Set radio */ |
| if (option < 0 || option > 1) { |
| PRINTM(MERROR, "Invalid arguments!\n"); |
| ret = -EINVAL; |
| goto done; |
| } |
| if (MLAN_STATUS_SUCCESS != woal_set_radio(priv, (t_u8)option)) |
| ret = -EFAULT; |
| goto done; |
| } else { |
| /* Get radio status */ |
| woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info); |
| memcpy(respbuf, &bss_info.radio_on, sizeof(bss_info.radio_on)); |
| ret = sizeof(bss_info.radio_on); |
| } |
| done: |
| LEAVE(); |
| return ret; |
| } |
| #endif |
| |
| /** |
| * @brief Implement WMM enable command |
| * |
| * @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. |
| */ |
| static int |
| woal_priv_wmm_cfg(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| int data = 0; |
| mlan_ds_wmm_cfg *wmm = NULL; |
| mlan_ioctl_req *req = NULL; |
| int ret = 0; |
| int user_data_len = 0, header_len = 0; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| |
| req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_wmm_cfg)); |
| if (req == NULL) { |
| ret = ENOMEM; |
| goto done; |
| } |
| |
| wmm = (mlan_ds_wmm_cfg *)req->pbuf; |
| wmm->sub_command = MLAN_OID_WMM_CFG_ENABLE; |
| req->req_id = MLAN_IOCTL_WMM_CFG; |
| |
| header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_WMM_CFG); |
| |
| if (strlen(respbuf) == header_len) { |
| /* GET operation */ |
| req->action = MLAN_ACT_GET; |
| user_data_len = 0; |
| } else { |
| /* SET operation */ |
| parse_arguments(respbuf + header_len, &data, 1, &user_data_len); |
| if (user_data_len == 1) { |
| /* Set wmm */ |
| if ((data < CMD_DISABLED) || (data > CMD_ENABLED)) { |
| PRINTM(MERROR, "Invalid arguments!\n"); |
| 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 { |
| PRINTM(MERROR, "Too many arguments\n"); |
| ret = -EINVAL; |
| goto done; |
| } |
| } |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| goto done; |
| } |
| |
| memcpy(respbuf, &wmm->param.wmm_enable, sizeof(wmm->param.wmm_enable)); |
| ret = sizeof(wmm->param.wmm_enable); |
| |
| done: |
| if (status != MLAN_STATUS_PENDING) |
| kfree(req); |
| LEAVE(); |
| return ret; |
| } |
| |
| #if defined(STA_SUPPORT) |
| /** |
| * @brief Implement 802.11D enable command |
| * |
| * @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. |
| */ |
| static int |
| woal_priv_11d_cfg(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| int data = 0; |
| mlan_ds_11d_cfg *pcfg_11d = NULL; |
| mlan_ioctl_req *req = NULL; |
| int ret = 0; |
| int user_data_len = 0, header_len = 0; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| |
| req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11d_cfg)); |
| if (req == NULL) { |
| ret = ENOMEM; |
| goto done; |
| } |
| |
| pcfg_11d = (mlan_ds_11d_cfg *)req->pbuf; |
| pcfg_11d->sub_command = MLAN_OID_11D_CFG_ENABLE; |
| req->req_id = MLAN_IOCTL_11D_CFG; |
| |
| header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_11D_CFG); |
| |
| if (strlen(respbuf) == header_len) { |
| /* GET operation */ |
| req->action = MLAN_ACT_GET; |
| user_data_len = 0; |
| } else { |
| /* SET operation */ |
| parse_arguments(respbuf + header_len, &data, 1, &user_data_len); |
| if (user_data_len == 1) { |
| if ((data < CMD_DISABLED) || (data > CMD_ENABLED)) { |
| PRINTM(MERROR, "Invalid arguments!\n"); |
| ret = -EINVAL; |
| goto done; |
| } |
| req->action = MLAN_ACT_SET; |
| if (data == CMD_DISABLED) |
| pcfg_11d->param.enable_11d = MFALSE; |
| else |
| pcfg_11d->param.enable_11d = MTRUE; |
| } else { |
| PRINTM(MERROR, "Too many arguments\n"); |
| ret = -EINVAL; |
| goto done; |
| } |
| } |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| goto done; |
| } |
| |
| memcpy(respbuf, &pcfg_11d->param.enable_11d, |
| sizeof(pcfg_11d->param.enable_11d)); |
| ret = sizeof(pcfg_11d->param.enable_11d); |
| |
| done: |
| if (status != MLAN_STATUS_PENDING) |
| kfree(req); |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief Implement 802.11D clear chan table command |
| * |
| * @param priv A pointer to moal_private structure |
| * @param respbuf A pointer to response buffer |
| * @param respbuflen Available length of response buffer |
| * |
| * @return 0 --success, otherwise fail |
| */ |
| static int |
| woal_priv_11d_clr_chan_tbl(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| mlan_ds_11d_cfg *pcfg_11d = NULL; |
| mlan_ioctl_req *req = NULL; |
| int ret = 0; |
| int header_len = 0; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| |
| req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11d_cfg)); |
| if (req == NULL) { |
| ret = ENOMEM; |
| goto done; |
| } |
| |
| header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_11D_CLR_TBL); |
| |
| if (strlen(respbuf) != header_len) { |
| PRINTM(MERROR, "Too many arguments\n"); |
| ret = -EINVAL; |
| goto done; |
| } |
| pcfg_11d = (mlan_ds_11d_cfg *)req->pbuf; |
| pcfg_11d->sub_command = MLAN_OID_11D_CLR_CHAN_TABLE; |
| req->req_id = MLAN_IOCTL_11D_CFG; |
| req->action = MLAN_ACT_SET; |
| |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| goto done; |
| } |
| |
| done: |
| if (status != MLAN_STATUS_PENDING) |
| kfree(req); |
| LEAVE(); |
| return ret; |
| } |
| #endif |
| |
| /** |
| * @brief Set/Get WWS mode |
| * |
| * @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. |
| */ |
| static int |
| woal_priv_wws_cfg(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| int data = 0; |
| mlan_ds_misc_cfg *wws = NULL; |
| mlan_ioctl_req *req = NULL; |
| int ret = 0; |
| int user_data_len = 0, header_len = 0; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| |
| req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); |
| if (req == NULL) { |
| ret = ENOMEM; |
| goto done; |
| } |
| |
| wws = (mlan_ds_misc_cfg *)req->pbuf; |
| wws->sub_command = MLAN_OID_MISC_WWS; |
| req->req_id = MLAN_IOCTL_MISC_CFG; |
| |
| header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_WWS_CFG); |
| |
| if (strlen(respbuf) == header_len) { |
| /* GET operation */ |
| req->action = MLAN_ACT_GET; |
| user_data_len = 0; |
| } else { |
| /* SET operation */ |
| parse_arguments(respbuf + header_len, &data, 1, &user_data_len); |
| if (user_data_len == 1) { |
| if ((data < CMD_DISABLED) || (data > CMD_ENABLED)) { |
| PRINTM(MERROR, |
| "Invalid arguments, WWS config not changed!\n"); |
| ret = -EINVAL; |
| goto done; |
| } |
| req->action = MLAN_ACT_SET; |
| wws->param.wws_cfg = (t_u16)data; |
| } else { |
| PRINTM(MERROR, "Too many arguments\n"); |
| ret = -EINVAL; |
| goto done; |
| } |
| } |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| goto done; |
| } |
| |
| memcpy(respbuf, &wws->param.wws_cfg, sizeof(wws->param.wws_cfg)); |
| ret = sizeof(wws->param.wws_cfg); |
| |
| done: |
| if (status != MLAN_STATUS_PENDING) |
| kfree(req); |
| LEAVE(); |
| return ret; |
| } |
| |
| #if defined(REASSOCIATION) |
| /** |
| * @brief Set/Get reassociation settings |
| * |
| * @param priv A pointer to moal_private structure |
| * @param respbuf A pointer to response buffer |
| * @param respbuflen Available length of response buffer |
| * |
| * @return Number of bytes written, negative for failure. |
| */ |
| static int |
| woal_priv_set_get_reassoc(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| moal_handle *handle = priv->phandle; |
| int data = 0; |
| int ret = 0; |
| int user_data_len = 0, header_len = 0; |
| |
| ENTER(); |
| |
| header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_REASSOCTRL); |
| |
| if (strlen(respbuf) == header_len) { |
| /* GET operation */ |
| user_data_len = 0; |
| data = (int)(priv->reassoc_on); |
| memcpy(respbuf, &data, sizeof(data)); |
| ret = sizeof(data); |
| } else { |
| /* SET operation */ |
| parse_arguments(respbuf + header_len, &data, 1, &user_data_len); |
| if (user_data_len == 1) { |
| 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 if (data == 1) { |
| handle->reassoc_on |= MBIT(priv->bss_index); |
| priv->reassoc_on = MTRUE; |
| } else { |
| PRINTM(MERROR, "Invalid arguments!\n"); |
| ret = -EINVAL; |
| } |
| } else { |
| PRINTM(MERROR, "Too many arguments\n"); |
| ret = -EINVAL; |
| } |
| } |
| |
| LEAVE(); |
| return ret; |
| } |
| #endif /* REASSOCIATION */ |
| |
| /** |
| * @brief Get Transmit buffer size |
| * |
| * @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. |
| */ |
| static int |
| woal_priv_txbuf_cfg(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| mlan_ds_11n_cfg *cfg_11n = NULL; |
| mlan_ioctl_req *req = NULL; |
| int ret = 0; |
| int buf_size = 0, header_len = 0; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| |
| req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); |
| if (req == NULL) { |
| ret = ENOMEM; |
| goto done; |
| } |
| |
| cfg_11n = (mlan_ds_11n_cfg *)req->pbuf; |
| cfg_11n->sub_command = MLAN_OID_11N_CFG_MAX_TX_BUF_SIZE; |
| req->req_id = MLAN_IOCTL_11N_CFG; |
| |
| header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_TXBUF_CFG); |
| |
| if (strlen(respbuf) != header_len) { |
| PRINTM(MERROR, |
| "Don't support set Tx buffer size after driver loaded!\n"); |
| ret = -EINVAL; |
| goto done; |
| } else { |
| /* Get Tx buffer size from MLAN */ |
| req->action = MLAN_ACT_GET; |
| } |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| goto done; |
| } |
| |
| buf_size = cfg_11n->param.tx_buf_size; |
| memcpy(respbuf, &buf_size, sizeof(buf_size)); |
| ret = sizeof(buf_size); |
| |
| done: |
| if (status != MLAN_STATUS_PENDING) |
| kfree(req); |
| LEAVE(); |
| return ret; |
| } |
| |
| #ifdef STA_SUPPORT |
| /** |
| * @brief Set/Get auth type |
| * |
| * @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. |
| */ |
| static int |
| woal_priv_auth_type(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| int auth_type; |
| t_u32 auth_mode; |
| int ret = 0; |
| int user_data_len = 0, header_len = 0; |
| |
| ENTER(); |
| |
| header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_AUTH_TYPE); |
| |
| if (strlen(respbuf) == header_len) { |
| /* GET operation */ |
| if (MLAN_STATUS_SUCCESS != |
| woal_get_auth_mode(priv, MOAL_IOCTL_WAIT, &auth_mode)) { |
| ret = -EFAULT; |
| goto done; |
| } |
| user_data_len = 0; |
| auth_type = auth_mode; |
| memcpy(respbuf, &auth_type, sizeof(auth_type)); |
| ret = sizeof(auth_type); |
| goto done; |
| } else { |
| /* SET operation */ |
| parse_arguments(respbuf + header_len, &auth_type, 1, |
| &user_data_len); |
| if (user_data_len == 1) { |
| 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; |
| } |
| } else { |
| PRINTM(MERROR, "Too many arguments\n"); |
| ret = -EINVAL; |
| } |
| } |
| |
| done: |
| LEAVE(); |
| return ret; |
| } |
| #endif |
| |
| /** |
| * @brief Set/get user provisioned local power constraint |
| * |
| * @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. |
| */ |
| static int |
| woal_priv_11h_local_pwr_constraint(moal_private *priv, t_u8 *respbuf, |
| t_u32 respbuflen) |
| { |
| int data = 0; |
| mlan_ds_11h_cfg *ds_11hcfg = NULL; |
| mlan_ioctl_req *req = NULL; |
| int ret = 0; |
| int user_data_len = 0, header_len = 0; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| |
| req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11h_cfg)); |
| if (req == NULL) { |
| ret = ENOMEM; |
| goto done; |
| } |
| |
| ds_11hcfg = (mlan_ds_11h_cfg *)req->pbuf; |
| ds_11hcfg->sub_command = MLAN_OID_11H_LOCAL_POWER_CONSTRAINT; |
| req->req_id = MLAN_IOCTL_11H_CFG; |
| |
| header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_POWER_CONS); |
| |
| if (strlen(respbuf) == header_len) { |
| /* GET operation */ |
| req->action = MLAN_ACT_GET; |
| user_data_len = 0; |
| } else { |
| /* SET operation */ |
| parse_arguments(respbuf + header_len, &data, 1, &user_data_len); |
| if (user_data_len == 1) { |
| req->action = MLAN_ACT_SET; |
| ds_11hcfg->param.usr_local_power_constraint = |
| (t_s8)data; |
| } else { |
| PRINTM(MERROR, "Too many arguments\n"); |
| ret = -EINVAL; |
| goto done; |
| } |
| } |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| goto done; |
| } |
| |
| if (req->action == MLAN_ACT_GET) { |
| data = (int)ds_11hcfg->param.usr_local_power_constraint; |
| memcpy(respbuf, &data, sizeof(data)); |
| ret = sizeof(data); |
| } |
| |
| done: |
| if (status != MLAN_STATUS_PENDING) |
| kfree(req); |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief Get thermal reading |
| * |
| * @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. |
| */ |
| static int |
| woal_priv_thermal(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| mlan_ds_misc_cfg *cfg = NULL; |
| mlan_ioctl_req *req = NULL; |
| int ret = 0, header_len = 0, data = 0; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| |
| req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); |
| if (req == NULL) { |
| ret = ENOMEM; |
| goto done; |
| } |
| |
| cfg = (mlan_ds_misc_cfg *)req->pbuf; |
| cfg->sub_command = MLAN_OID_MISC_THERMAL; |
| req->req_id = MLAN_IOCTL_MISC_CFG; |
| |
| header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_THERMAL); |
| |
| if (strlen(respbuf) != header_len) { |
| PRINTM(MERROR, "Set is not supported for this command\n"); |
| ret = -EINVAL; |
| goto done; |
| } |
| req->action = MLAN_ACT_GET; |
| |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| goto done; |
| } |
| |
| data = (int)cfg->param.thermal; |
| memcpy(respbuf, &data, sizeof(data)); |
| ret = sizeof(data); |
| |
| done: |
| if (status != MLAN_STATUS_PENDING) |
| kfree(req); |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief Set/Get beacon interval |
| * |
| * @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. |
| */ |
| static int |
| woal_priv_beacon_interval(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| int data = 0; |
| mlan_ds_bss *bss = NULL; |
| mlan_ioctl_req *req = NULL; |
| int ret = 0; |
| int user_data_len = 0, header_len = 0; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| |
| 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; |
| |
| header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_BCN_INTERVAL); |
| |
| if (strlen(respbuf) == header_len) { |
| /* GET operation */ |
| req->action = MLAN_ACT_GET; |
| user_data_len = 0; |
| } else { |
| /* SET operation */ |
| parse_arguments(respbuf + header_len, &data, 1, &user_data_len); |
| if (user_data_len == 1) { |
| if ((data < MLAN_MIN_BEACON_INTERVAL) || |
| (data > MLAN_MAX_BEACON_INTERVAL)) { |
| PRINTM(MERROR, "Invalid arguments!\n"); |
| ret = -EINVAL; |
| goto done; |
| } |
| req->action = MLAN_ACT_SET; |
| bss->param.bcn_interval = data; |
| } else { |
| PRINTM(MERROR, "Too many arguments\n"); |
| ret = -EINVAL; |
| goto done; |
| } |
| } |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| goto done; |
| } |
| |
| data = ((mlan_ds_bss *)req->pbuf)->param.bcn_interval; |
| memcpy(respbuf, &data, sizeof(data)); |
| ret = sizeof(data); |
| |
| done: |
| if (status != MLAN_STATUS_PENDING) |
| kfree(req); |
| LEAVE(); |
| return ret; |
| } |
| |
| #ifdef STA_SUPPORT |
| /** |
| * @brief Get signal |
| * |
| * @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. |
| */ |
| static int |
| woal_priv_get_signal(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| /** 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; |
| int user_data_len = 0, header_len = 0; |
| |
| ENTER(); |
| |
| memset(in_data, 0, sizeof(in_data)); |
| memset(out_data, 0, sizeof(out_data)); |
| |
| header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_GET_SIGNAL); |
| |
| if (strlen(respbuf) != header_len) |
| parse_arguments(respbuf + header_len, in_data, IN_DATA_SIZE, |
| &user_data_len); |
| buflen = MIN(user_data_len, IN_DATA_SIZE); |
| |
| if (priv->media_connected == MFALSE) { |
| PRINTM(MERROR, "Can not get RSSI in disconnected state\n"); |
| ret = -ENOTSUPP; |
| goto done; |
| } |
| |
| if (user_data_len) { |
| if (user_data_len > IN_DATA_SIZE) { |
| PRINTM(MERROR, "Too many arguments\n"); |
| ret = -EINVAL; |
| goto done; |
| } |
| } |
| |
| switch (user_data_len) { |
| 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; |
| } |
| |
| memcpy(respbuf, out_data, (data_length * sizeof(int))); |
| ret = data_length * sizeof(int); |
| |
| done: |
| LEAVE(); |
| return ret; |
| } |
| #endif |
| |
| #if defined(STA_SUPPORT) |
| /** |
| * @brief Make PMF bit required/optional |
| * @param priv Pointer to moal_private structure |
| * @param respbuf Pointer to response buffer |
| * @param resplen Response buffer length |
| * |
| * @return 0 -- success, otherwise fail |
| */ |
| int |
| woal_priv_set_get_pmfcfg(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| int data[2] = { 0, 0 }; |
| mlan_ioctl_req *req = NULL; |
| mlan_ds_misc_cfg *cfg = NULL; |
| mlan_ds_misc_pmfcfg *pmfcfg; |
| int ret = 0; |
| int user_data_len = 0, header_len = 0; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| |
| if (!priv->phandle->card_info->embedded_supp) { |
| PRINTM(MERROR, "Not supported cmd on this card\n"); |
| ret = -EOPNOTSUPP; |
| goto done; |
| } |
| |
| header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_PMFCFG); |
| if (strlen(respbuf) == header_len) { |
| /* GET operation */ |
| user_data_len = 0; |
| } else { |
| /* SET operation */ |
| parse_arguments(respbuf + header_len, data, ARRAY_SIZE(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_misc_cfg)); |
| if (req == NULL) { |
| ret = -ENOMEM; |
| goto done; |
| } |
| |
| cfg = (mlan_ds_misc_cfg *)req->pbuf; |
| pmfcfg = (mlan_ds_misc_pmfcfg *)&cfg->param.pmfcfg; |
| cfg->sub_command = MLAN_OID_MISC_PMFCFG; |
| req->req_id = MLAN_IOCTL_MISC_CFG; |
| |
| if (user_data_len == 0) |
| req->action = MLAN_ACT_GET; |
| else { |
| pmfcfg->mfpc = (t_u8)data[0]; |
| pmfcfg->mfpr = (t_u8)data[1]; |
| req->action = MLAN_ACT_SET; |
| } |
| |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| goto done; |
| } |
| |
| memcpy(respbuf, (t_u8 *)&cfg->param.pmfcfg, |
| sizeof(mlan_ds_misc_pmfcfg)); |
| ret = sizeof(mlan_ds_misc_pmfcfg); |
| |
| done: |
| if (status != MLAN_STATUS_PENDING) |
| kfree(req); |
| LEAVE(); |
| return ret; |
| } |
| #endif |
| |
| /** |
| * @brief Get/Set inactivity timeout extend |
| * @param priv Pointer to moal_private structure |
| * @param respbuf Pointer to response buffer |
| * @param resplen Response buffer length |
| * |
| * @return Number of bytes written, negative for failure. |
| */ |
| static int |
| woal_priv_inactivity_timeout_ext(moal_private *priv, t_u8 *respbuf, |
| t_u32 respbuflen) |
| { |
| int data[4]; |
| mlan_ioctl_req *req = NULL; |
| mlan_ds_pm_cfg *pmcfg = NULL; |
| pmlan_ds_inactivity_to inac_to = NULL; |
| int ret = 0; |
| int user_data_len = 0, header_len = 0; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| |
| header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_INACTIVITYTO); |
| memset(data, 0, sizeof(data)); |
| if (strlen(respbuf) == header_len) { |
| /* GET operation */ |
| user_data_len = 0; |
| } else { |
| /* SET operation */ |
| parse_arguments(respbuf + header_len, data, ARRAY_SIZE(data), |
| &user_data_len); |
| } |
| |
| if (user_data_len != 0 && user_data_len != 3 && user_data_len != 4) { |
| PRINTM(MERROR, "Invalid number of parameters\n"); |
| ret = -EINVAL; |
| goto done; |
| } |
| 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; |
| req->action = MLAN_ACT_GET; |
| |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| goto done; |
| } |
| |
| if (user_data_len) { |
| inac_to->timeout_unit = data[0]; |
| inac_to->unicast_timeout = data[1]; |
| inac_to->mcast_timeout = data[2]; |
| if (user_data_len == 4) |
| inac_to->ps_entry_timeout = data[3]; |
| req->action = MLAN_ACT_SET; |
| |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| goto done; |
| } |
| } else { |
| 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; |
| |
| memcpy(respbuf, (t_u8 *)data, sizeof(data)); |
| ret = sizeof(data); |
| } |
| |
| done: |
| if (status != MLAN_STATUS_PENDING) |
| kfree(req); |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief Enable/Disable amsdu_aggr_ctrl |
| * |
| * @param priv Pointer to moal_private structure |
| * @param respbuf Pointer to response buffer |
| * @param resplen Response buffer length |
| * |
| * @return Number of bytes written, negative for failure. |
| */ |
| static int |
| woal_priv_11n_amsdu_aggr_ctrl(moal_private *priv, t_u8 *respbuf, |
| t_u32 respbuflen) |
| { |
| mlan_ioctl_req *req = NULL; |
| mlan_ds_11n_cfg *cfg_11n = NULL; |
| int ret = 0, data[2]; |
| int user_data_len = 0, header_len = 0; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| |
| header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_AMSDU_AGGR_CTRL); |
| 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 (strlen(respbuf) == header_len) { |
| /* GET operation */ |
| user_data_len = 0; |
| req->action = MLAN_ACT_GET; |
| } else { |
| /* SET operation */ |
| parse_arguments(respbuf + header_len, data, 1, &user_data_len); |
| |
| if (user_data_len != 1) { |
| PRINTM(MERROR, "Invalid number of parameters\n"); |
| ret = -EINVAL; |
| goto done; |
| } |
| req->action = MLAN_ACT_SET; |
| cfg_11n->param.amsdu_aggr_ctrl.enable = data[0]; |
| } |
| |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| goto done; |
| } |
| |
| data[0] = cfg_11n->param.amsdu_aggr_ctrl.enable; |
| data[1] = cfg_11n->param.amsdu_aggr_ctrl.curr_buf_size; |
| |
| memcpy(respbuf, (t_u8 *)data, sizeof(data)); |
| ret = sizeof(data); |
| |
| done: |
| if (status != MLAN_STATUS_PENDING) |
| kfree(req); |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief Set/Get Transmit beamforming capabilities |
| * |
| * @param priv Pointer to moal_private structure |
| * @param respbuf Pointer to response buffer |
| * @param resplen Response buffer length |
| * |
| * @return Number of bytes written, negative for failure. |
| */ |
| static int |
| woal_priv_tx_bf_cap_ioctl(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| mlan_ioctl_req *req = NULL; |
| mlan_ds_11n_cfg *bf_cfg = NULL; |
| int ret = 0, bf_cap = 0; |
| int user_data_len = 0, header_len = 0; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| |
| header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_TX_BF_CAP); |
| req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); |
| if (req == NULL) { |
| ret = -ENOMEM; |
| goto done; |
| } |
| |
| bf_cfg = (mlan_ds_11n_cfg *)req->pbuf; |
| bf_cfg->sub_command = MLAN_OID_11N_CFG_TX_BF_CAP; |
| req->req_id = MLAN_IOCTL_11N_CFG; |
| |
| if (strlen(respbuf) == header_len) { |
| /* GET operation */ |
| user_data_len = 0; |
| req->action = MLAN_ACT_GET; |
| } else { |
| /* SET operation */ |
| parse_arguments(respbuf + header_len, &bf_cap, 1, |
| &user_data_len); |
| |
| if (user_data_len != 1) { |
| PRINTM(MERROR, "Invalid number of parameters\n"); |
| ret = -EINVAL; |
| goto done; |
| } |
| req->action = MLAN_ACT_SET; |
| bf_cfg->param.tx_bf_cap = bf_cap; |
| } |
| |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| goto done; |
| } |
| |
| bf_cap = bf_cfg->param.tx_bf_cap; |
| |
| memcpy(respbuf, (t_u8 *)&bf_cap, sizeof(bf_cap)); |
| ret = sizeof(bf_cap); |
| |
| done: |
| if (status != MLAN_STATUS_PENDING) |
| kfree(req); |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief Turn on/off the sdio clock |
| * |
| * @param priv Pointer to moal_private structure |
| * @param respbuf Pointer to response buffer |
| * @param resplen Response buffer length |
| * |
| * @return Number of bytes written, negative for failure. |
| */ |
| static int |
| woal_priv_sdio_clock_ioctl(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| int ret = 0; |
| int data = 2; |
| int user_data_len = 0, header_len = 0; |
| /* Initialize the clock state as on */ |
| static int clock_state = 1; |
| |
| ENTER(); |
| |
| header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_SDIO_CLOCK); |
| if (strlen(respbuf) == header_len) { |
| /* GET operation */ |
| memcpy(respbuf, (t_u8 *)&clock_state, sizeof(clock_state)); |
| ret = sizeof(clock_state); |
| goto done; |
| } else { |
| /* SET operation */ |
| parse_arguments(respbuf + header_len, &data, 1, &user_data_len); |
| |
| if (user_data_len != 1) { |
| PRINTM(MERROR, "Invalid number of parameters\n"); |
| ret = -EINVAL; |
| 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; |
| } |
| |
| #if defined(SDIO_MULTI_PORT_TX_AGGR) || defined(SDIO_MULTI_PORT_RX_AGGR) |
| /** |
| * @brief Set SDIO Multi-point aggregation control parameters |
| * |
| * @param priv Pointer to moal_private structure |
| * @param respbuf Pointer to response buffer |
| * @param resplen Response buffer length |
| * |
| * @return Number of bytes written, negative for failure. |
| */ |
| static int |
| woal_priv_sdio_mpa_ctrl(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| mlan_ioctl_req *req = NULL; |
| mlan_ds_misc_cfg *misc = NULL; |
| int ret = 0, data[6]; |
| int user_data_len = 0, header_len = 0; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| |
| memset(data, 0, sizeof(data)); |
| header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_MPA_CTRL); |
| if (strlen(respbuf) == header_len) { |
| /* GET operation */ |
| user_data_len = 0; |
| } else { |
| /* SET operation */ |
| parse_arguments(respbuf + header_len, data, ARRAY_SIZE(data), |
| &user_data_len); |
| |
| if (user_data_len > 6) { |
| PRINTM(MERROR, "Invalid number of parameters\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; |
| misc->sub_command = MLAN_OID_MISC_SDIO_MPA_CTRL; |
| req->req_id = MLAN_IOCTL_MISC_CFG; |
| req->action = MLAN_ACT_GET; |
| /* Get the values first, then modify these values if user had modified |
| them */ |
| |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| PRINTM(MERROR, "woal_request_ioctl returned %d\n", ret); |
| ret = -EFAULT; |
| goto done; |
| } |
| |
| if (user_data_len == 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]); |
| |
| memcpy(respbuf, (t_u8 *)data, sizeof(data)); |
| ret = sizeof(data); |
| goto done; |
| } |
| |
| switch (user_data_len) { |
| 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; |
| } |
| |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| goto done; |
| } |
| |
| done: |
| if (status != MLAN_STATUS_PENDING) |
| kfree(req); |
| LEAVE(); |
| return ret; |
| } |
| #endif |
| |
| /** |
| * @brief Configure sleep parameters |
| * |
| * @param priv Pointer to moal_private structure |
| * @param respbuf Pointer to response buffer |
| * @param resplen Response buffer length |
| * |
| * @return Number of bytes written, negative for failure. |
| */ |
| static int |
| woal_priv_sleep_params_ioctl(moal_private *priv, t_u8 *respbuf, |
| t_u32 respbuflen) |
| { |
| 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; |
| int user_data_len = 0, header_len = 0; |
| char err_str[][35] = { {"sleep clock error in ppm"}, |
| {"wakeup offset in usec"}, |
| {"clock stabilization time in usec"}, |
| {"value of reserved for debug"} |
| }; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| |
| memset(data, 0, sizeof(data)); |
| req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_pm_cfg)); |
| if (req == NULL) { |
| ret = -ENOMEM; |
| goto done; |
| } |
| 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; |
| |
| header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_SLEEP_PARAMS); |
| if (strlen(respbuf) == header_len) { |
| /* GET operation */ |
| user_data_len = 0; |
| req->action = MLAN_ACT_GET; |
| } else { |
| /* SET operation */ |
| parse_arguments(respbuf + header_len, data, ARRAY_SIZE(data), |
| &user_data_len); |
| if (user_data_len != 6) { |
| PRINTM(MERROR, "Invalid number of parameters\n"); |
| ret = -EINVAL; |
| goto done; |
| } |
| #define MIN_VAL 0x0000 |
| #define MAX_VAL 0xFFFF |
| for (i = 0; i < 6; i++) { |
| if ((i == 3) || (i == 4)) { |
| /* These two cases are handled below the loop */ |
| continue; |
| } |
| if (data[i] < MIN_VAL || data[i] > MAX_VAL) { |
| PRINTM(MERROR, "Invalid %s (0-65535)!\n", |
| err_str[i]); |
| ret = -EINVAL; |
| goto done; |
| } |
| } |
| if (data[3] < 0 || data[3] > 2) { |
| PRINTM(MERROR, |
| "Invalid control periodic calibration (0-2)!\n"); |
| ret = -EINVAL; |
| goto done; |
| } |
| if (data[4] < 0 || data[4] > 2) { |
| PRINTM(MERROR, |
| "Invalid control of external sleep clock (0-2)!\n"); |
| ret = -EINVAL; |
| goto done; |
| } |
| req->action = MLAN_ACT_SET; |
| psleep_params->error = data[0]; |
| psleep_params->offset = data[1]; |
| psleep_params->stable_time = data[2]; |
| psleep_params->cal_control = data[3]; |
| psleep_params->ext_sleep_clk = data[4]; |
| psleep_params->reserved = data[5]; |
| } |
| |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| goto done; |
| } |
| |
| data[0] = psleep_params->error; |
| data[1] = psleep_params->offset; |
| data[2] = psleep_params->stable_time; |
| data[3] = psleep_params->cal_control; |
| data[4] = psleep_params->ext_sleep_clk; |
| data[5] = psleep_params->reserved; |
| |
| memcpy(respbuf, (t_u8 *)data, sizeof(data)); |
| ret = sizeof(data); |
| |
| done: |
| if (status != MLAN_STATUS_PENDING) |
| kfree(req); |
| |
| LEAVE(); |
| return ret; |
| } |
| |
| #if defined(DFS_TESTING_SUPPORT) |
| /** |
| * @brief Set/Get DFS Testing settings |
| * |
| * @param priv Pointer to moal_private structure |
| * @param respbuf Pointer to response buffer |
| * @param resplen Response buffer length |
| * |
| * @return Number of bytes written, negative for failure. |
| */ |
| static int |
| woal_priv_dfs_testing(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| mlan_ioctl_req *req = NULL; |
| mlan_ds_11h_cfg *ds_11hcfg = NULL; |
| int ret = 0; |
| int data[4]; |
| int user_data_len = 0, header_len = 0; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| |
| header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_DFS_TESTING); |
| /* 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 (strlen(respbuf) == header_len) { |
| /* GET operation */ |
| user_data_len = 0; |
| req->action = MLAN_ACT_GET; |
| } else { |
| /* SET operation */ |
| parse_arguments(respbuf + header_len, data, ARRAY_SIZE(data), |
| &user_data_len); |
| if (user_data_len != 4) { |
| PRINTM(MERROR, "Invalid number of args!\n"); |
| ret = -EINVAL; |
| 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; |
| } |
| |
| /* Send IOCTL request to MLAN */ |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| goto done; |
| } |
| |
| if (!user_data_len) { |
| 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; |
| memcpy(respbuf, (t_u8 *)data, sizeof(data)); |
| ret = sizeof(data); |
| } |
| |
| done: |
| if (status != MLAN_STATUS_PENDING) |
| kfree(req); |
| |
| LEAVE(); |
| return ret; |
| } |
| #endif /* DFS_SUPPORT && DFS_TESTING_SUPPORT */ |
| |
| /** |
| * @brief Set/Get CFP table codes |
| * |
| * @param priv Pointer to moal_private structure |
| * @param respbuf Pointer to response buffer |
| * @param resplen Response buffer length |
| * |
| * @return Number of bytes written, negative for failure. |
| */ |
| static int |
| woal_priv_cfp_code(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| int ret = 0; |
| int user_data_len = 0, header_len = 0; |
| int data[2]; |
| mlan_ioctl_req *req = NULL; |
| mlan_ds_misc_cfg *misc_cfg = NULL; |
| mlan_ds_misc_cfp_code *cfp_code = NULL; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| |
| header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_CFP_CODE); |
| |
| /* Allocate an IOCTL request buffer */ |
| req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); |
| if (req == NULL) { |
| ret = -ENOMEM; |
| goto done; |
| } |
| |
| /* Fill request buffer */ |
| misc_cfg = (mlan_ds_misc_cfg *)req->pbuf; |
| cfp_code = &misc_cfg->param.cfp_code; |
| misc_cfg->sub_command = MLAN_OID_MISC_CFP_CODE; |
| req->req_id = MLAN_IOCTL_MISC_CFG; |
| |
| if (strlen(respbuf) == header_len) { |
| /* GET operation */ |
| user_data_len = 0; |
| req->action = MLAN_ACT_GET; |
| } else { |
| /* SET operation */ |
| parse_arguments(respbuf + header_len, data, ARRAY_SIZE(data), |
| &user_data_len); |
| if (user_data_len > 2) { |
| PRINTM(MERROR, "Invalid number of args!\n"); |
| ret = -EINVAL; |
| goto done; |
| } |
| cfp_code->cfp_code_bg = data[0]; |
| if (user_data_len == 2) |
| cfp_code->cfp_code_a = data[1]; |
| req->action = MLAN_ACT_SET; |
| } |
| |
| /* Send IOCTL request to MLAN */ |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| goto done; |
| } |
| |
| if (!user_data_len) { |
| data[0] = cfp_code->cfp_code_bg; |
| data[1] = cfp_code->cfp_code_a; |
| memcpy(respbuf, (t_u8 *)data, sizeof(data)); |
| ret = sizeof(data); |
| } |
| |
| done: |
| if (status != MLAN_STATUS_PENDING) |
| kfree(req); |
| |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief Set/Get Tx/Rx antenna |
| * |
| * @param priv Pointer to moal_private structure |
| * @param respbuf Pointer to response buffer |
| * @param resplen Response buffer length |
| * |
| * @return Number of bytes written, negative for failure. |
| */ |
| static int |
| woal_priv_set_get_tx_rx_ant(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| int ret = 0; |
| int user_data_len = 0, header_len = 0; |
| mlan_ds_radio_cfg *radio = NULL; |
| mlan_ioctl_req *req = NULL; |
| int data = 0; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| |
| header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_ANT_CFG); |
| 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 (strlen(respbuf) == header_len) { |
| /* GET operation */ |
| user_data_len = 0; |
| req->action = MLAN_ACT_GET; |
| } else { |
| /* SET operation */ |
| parse_arguments(respbuf + header_len, &data, 1, &user_data_len); |
| if (user_data_len > 1) { |
| PRINTM(MERROR, "Invalid number of args!\n"); |
| ret = -EINVAL; |
| goto done; |
| } |
| radio->param.antenna = data; |
| req->action = MLAN_ACT_SET; |
| } |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| goto done; |
| } |
| if (!user_data_len) { |
| data = radio->param.antenna; |
| memcpy(respbuf, (t_u8 *)&data, sizeof(data)); |
| ret = sizeof(data); |
| } |
| done: |
| if (status != MLAN_STATUS_PENDING) |
| kfree(req); |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief Get/Set system clock |
| * @param priv Pointer to moal_private structure |
| * @param respbuf Pointer to response buffer |
| * @param resplen Response buffer length |
| * |
| * @return Number of bytes written, negative for failure. |
| */ |
| static int |
| woal_priv_sysclock(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| int data[65]; |
| mlan_ioctl_req *req = NULL; |
| mlan_ds_misc_cfg *cfg = NULL; |
| int ret = 0, i = 0; |
| int user_data_len = 0, header_len = 0; |
| int data_length = 0, length_index = 0; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| |
| header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_SYSCLOCK); |
| memset(data, 0, sizeof(data)); |
| if (strlen(respbuf) == header_len) { |
| /* GET operation */ |
| user_data_len = 0; |
| } else { |
| /* SET operation */ |
| parse_arguments(respbuf + header_len, data, ARRAY_SIZE(data), |
| &user_data_len); |
| } |
| |
| if (user_data_len > MLAN_MAX_CLK_NUM) { |
| PRINTM(MERROR, "Invalid number of parameters\n"); |
| ret = -EINVAL; |
| goto done; |
| } |
| req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); |
| if (req == NULL) { |
| ret = -ENOMEM; |
| goto done; |
| } |
| |
| cfg = (mlan_ds_misc_cfg *)req->pbuf; |
| cfg->sub_command = MLAN_OID_MISC_SYS_CLOCK; |
| req->req_id = MLAN_IOCTL_MISC_CFG; |
| |
| if (user_data_len) { |
| /* SET operation */ |
| req->action = MLAN_ACT_SET; |
| |
| /* 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, user_data_len); |
| for (i = 0; i < cfg->param.sys_clock.sys_clk_num; i++) |
| cfg->param.sys_clock.sys_clk[i] = (t_u16)data[i]; |
| |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| goto done; |
| } |
| } else { |
| /* GET operation */ |
| req->action = MLAN_ACT_GET; |
| |
| /* Get configurable clocks */ |
| cfg->param.sys_clock.sys_clk_type = MLAN_CLK_CONFIGURABLE; |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| goto done; |
| } |
| |
| /* Current system clock */ |
| data[1] = (int)cfg->param.sys_clock.cur_sys_clk; |
| data_length = 1; |
| |
| length_index = |
| MIN(cfg->param.sys_clock.sys_clk_num, MLAN_MAX_CLK_NUM); |
| |
| /* Configurable clocks */ |
| for (i = 1; i <= length_index; i++) |
| data[i + data_length] = |
| (int)cfg->param.sys_clock.sys_clk[i - 1]; |
| |
| data_length += length_index; |
| |
| /* Get supported clocks */ |
| cfg->param.sys_clock.sys_clk_type = MLAN_CLK_SUPPORTED; |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| goto done; |
| } |
| |
| length_index = |
| MIN(cfg->param.sys_clock.sys_clk_num, MLAN_MAX_CLK_NUM); |
| |
| /* Supported clocks */ |
| for (i = 1; i <= length_index; i++) |
| data[i + data_length] = |
| (int)cfg->param.sys_clock.sys_clk[i - 1]; |
| |
| data_length += length_index; |
| |
| /* Send length as first element */ |
| data[0] = data_length; |
| data_length++; |
| |
| memcpy(respbuf, data, sizeof(int) * data_length); |
| ret = data_length * sizeof(int); |
| } |
| |
| done: |
| if (status != MLAN_STATUS_PENDING) |
| kfree(req); |
| LEAVE(); |
| return ret; |
| } |
| |
| /** AES key length */ |
| #define AES_KEY_LEN 16 |
| /** |
| * @brief Adhoc AES control |
| * @param priv Pointer to moal_private structure |
| * @param respbuf Pointer to response buffer |
| * @param resplen Response buffer length |
| * |
| * @return Number of bytes written, negative for failure. |
| */ |
| static int |
| woal_priv_adhoc_aes(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| 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 }; |
| int header_len = 0; |
| int copy_len = 0; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| |
| header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_ADHOC_AES); |
| memset(key_ascii, 0x00, sizeof(key_ascii)); |
| memset(key_hex, 0x00, sizeof(key_hex)); |
| memset(buf, 0x00, sizeof(buf)); |
| |
| /* 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 (strlen(respbuf) == header_len) { |
| /* 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; |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| 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)); |
| |
| tmp = key_ascii; |
| for (i = 0; i < sizeof(key_hex); i++) |
| tmp += sprintf((char *)tmp, "%02x", key_hex[i]); |
| } else { |
| /* SET operation */ |
| copy_len = (strlen(respbuf) - header_len); |
| if (copy_len >= sizeof(buf)) { |
| PRINTM(MERROR, "Too many arguments\n"); |
| ret = -EINVAL; |
| goto done; |
| } |
| memcpy(buf, respbuf + header_len, copy_len); |
| |
| /* 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); |
| |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| goto done; |
| } |
| } else { |
| /* 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)); |
| |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| goto done; |
| } |
| } |
| } |
| |
| HEXDUMP("Adhoc AES Key (ASCII)", key_ascii, sizeof(key_ascii)); |
| copy_len = sizeof(key_ascii); |
| memcpy(respbuf, &key_ascii, copy_len); |
| ret = copy_len; |
| |
| done: |
| if (status != MLAN_STATUS_PENDING) |
| kfree(req); |
| |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief Get GTK/PTK |
| * @param priv Pointer to moal_private structure |
| * @param respbuf Pointer to response buffer |
| * @param resplen Response buffer length |
| * |
| * @return Number of bytes written, negative for failure. |
| */ |
| static int |
| woal_priv_get_key(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| int ret = 0, copy_len = 0; |
| int header_len = 0; |
| unsigned int i; |
| t_u8 key_ascii[256]; |
| t_u8 *tmp; |
| mlan_ds_sec_cfg *sec = NULL; |
| mlan_ioctl_req *req = NULL; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| |
| header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_GET_KEY); |
| if (strlen(respbuf) != header_len) { |
| PRINTM(MERROR, "Invalid number of parameters\n"); |
| ret = -EINVAL; |
| goto done; |
| } |
| memset(key_ascii, 0x00, sizeof(key_ascii)); |
| tmp = key_ascii; |
| |
| if (priv->media_connected == MFALSE) { |
| PRINTM(MERROR, "Can't get key in un-associated state\n"); |
| ret = -EFAULT; |
| goto done; |
| } |
| |
| req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg)); |
| if (req == NULL) { |
| ret = -ENOMEM; |
| goto done; |
| } |
| |
| /* Get Unicast Key */ |
| req->req_id = MLAN_IOCTL_SEC_CFG; |
| req->action = MLAN_ACT_GET; |
| sec = (mlan_ds_sec_cfg *)req->pbuf; |
| sec->sub_command = MLAN_OID_SEC_QUERY_KEY; |
| sec->param.encrypt_key.key_index = 0; |
| sec->param.encrypt_key.key_flags = 0; |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| goto done; |
| } |
| if (sec->param.encrypt_key.key_len) { |
| sprintf((char *)tmp, "\n%s", "PTK: "); |
| tmp += 5; |
| for (i = 0; i < sec->param.encrypt_key.key_len; i++) |
| tmp += sprintf((char *)tmp, "%02x", |
| sec->param.encrypt_key.key_material[i]); |
| } |
| |
| /* Get Multicase Key */ |
| req->req_id = MLAN_IOCTL_SEC_CFG; |
| req->action = MLAN_ACT_GET; |
| sec = (mlan_ds_sec_cfg *)req->pbuf; |
| sec->sub_command = MLAN_OID_SEC_QUERY_KEY; |
| sec->param.encrypt_key.key_index = 0; |
| sec->param.encrypt_key.key_flags = KEY_FLAG_GROUP_KEY; |
| memset(sec->param.encrypt_key.mac_addr, 0x0, MLAN_MAC_ADDR_LENGTH); |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| goto done; |
| } |
| if (sec->param.encrypt_key.key_len) { |
| sprintf((char *)tmp, "\n%s", "GTK: "); |
| tmp += 5; |
| for (i = 0; i < sec->param.encrypt_key.key_len; i++) |
| tmp += sprintf((char *)tmp, "%02x", |
| sec->param.encrypt_key.key_material[i]); |
| } |
| |
| /* Get IGTK Key */ |
| req->req_id = MLAN_IOCTL_SEC_CFG; |
| req->action = MLAN_ACT_GET; |
| sec = (mlan_ds_sec_cfg *)req->pbuf; |
| sec->sub_command = MLAN_OID_SEC_QUERY_KEY; |
| sec->param.encrypt_key.key_index = 0; |
| sec->param.encrypt_key.key_flags = KEY_FLAG_AES_MCAST_IGTK; |
| memset(sec->param.encrypt_key.mac_addr, 0x0, MLAN_MAC_ADDR_LENGTH); |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| goto done; |
| } |
| if (sec->param.encrypt_key.key_len) { |
| sprintf((char *)tmp, "\n%s", "IGTK: "); |
| tmp += 6; |
| for (i = 0; i < sec->param.encrypt_key.key_len; i++) |
| tmp += sprintf((char *)tmp, "%02x", |
| sec->param.encrypt_key.key_material[i]); |
| } |
| |
| copy_len = tmp - key_ascii; |
| memcpy(respbuf, &key_ascii, copy_len); |
| ret = copy_len; |
| done: |
| if (status != MLAN_STATUS_PENDING) |
| kfree(req); |
| |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief Associate to a specific indexed entry in the ScanTable |
| * @param priv Pointer to moal_private structure |
| * @param respbuf Pointer to response buffer |
| * @param resplen Response buffer length |
| * |
| * @return Number of bytes written, negative for failure. |
| */ |
| static int |
| woal_priv_associate_ssid_bssid(moal_private *priv, t_u8 *respbuf, |
| t_u32 respbuflen) |
| { |
| int ret = 0, copy_len = 0; |
| int header_len = 0; |
| mlan_ssid_bssid ssid_bssid; |
| #ifdef REASSOCIATION |
| mlan_bss_info bss_info; |
| #endif |
| char buf[64]; |
| t_u8 buflen; |
| t_u8 mac_idx; |
| t_u8 i; |
| |
| ENTER(); |
| |
| header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_ASSOCIATE); |
| if (strlen(respbuf) == header_len) { |
| PRINTM(MERROR, "Invalid number of parameters\n"); |
| ret = -EINVAL; |
| goto done; |
| } |
| copy_len = strlen(respbuf) - header_len; |
| mac_idx = 0; |
| buflen = MIN(copy_len, (sizeof(buf) - 1)); |
| memset(buf, 0, sizeof(buf)); |
| |
| if (buflen < (3 * ETH_ALEN) + 2) { |
| PRINTM(MERROR, |
| "Associate: Insufficient length in IOCTL input\n"); |
| |
| /* buffer should be at least 3 characters per BSSID octet "00:" |
| ** plus a space separater and at least 1 char in the SSID */ |
| ret = -EINVAL; |
| goto done; |
| } |
| |
| memcpy(buf, respbuf + header_len, buflen); |
| |
| /* Skip white space */ |
| for (i = 0; (i < buflen) && (buf[i] == ' '); i++) ; |
| |
| /* Copy/Convert the BSSID */ |
| for (; (i < buflen) && (mac_idx < ETH_ALEN) && (buf[i] != ' '); i++) { |
| if (buf[i] == ':') { |
| mac_idx++; |
| } else { |
| ssid_bssid.bssid[mac_idx] = (t_u8)woal_atox(buf + i); |
| |
| while ((isxdigit(buf[i + 1]) && (i < buflen))) |
| /* Skip entire hex value */ |
| i++; |
| } |
| } |
| |
| /* Skip one space between the BSSID and start of the SSID */ |
| i++; |
| |
| /* Copy the SSID */ |
| ssid_bssid.ssid.ssid_len = buflen - i; |
| memcpy(ssid_bssid.ssid.ssid, buf + i, sizeof(ssid_bssid.ssid.ssid)); |
| |
| PRINTM(MCMND, "iwpriv assoc: AP=[" MACSTR "], ssid(%d)=[%s]\n", |
| MAC2STR(ssid_bssid.bssid), |
| (int)ssid_bssid.ssid.ssid_len, ssid_bssid.ssid.ssid); |
| |
| if (MLAN_STATUS_SUCCESS != woal_bss_start(priv, |
| MOAL_IOCTL_WAIT, |
| &ssid_bssid)) { |
| ret = -EFAULT; |
| goto done; |
| } |
| #ifdef REASSOCIATION |
| memset(&bss_info, 0x00, sizeof(bss_info)); |
| if (MLAN_STATUS_SUCCESS == woal_get_bss_info(priv, |
| MOAL_IOCTL_WAIT, |
| &bss_info)) { |
| memcpy(&priv->prev_ssid_bssid.ssid, |
| &bss_info.ssid, sizeof(mlan_802_11_ssid)); |
| memcpy(&priv->prev_ssid_bssid.bssid, |
| &bss_info.bssid, MLAN_MAC_ADDR_LENGTH); |
| } |
| #endif /* REASSOCIATION */ |
| |
| done: |
| LEAVE(); |
| return 0; |
| } |
| |
| /** |
| * @brief Cmd53 read/write register |
| * @param priv Pointer to moal_private structure |
| * @param respbuf Pointer to response buffer |
| * @param resplen Response buffer length |
| * |
| * @return Number of bytes written, negative for failure. |
| */ |
| static int |
| woal_priv_cmd53rdwr(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| int header_len = 0; |
| int ret = 0; |
| t_u8 *buf = NULL; |
| t_u8 *data = NULL; |
| t_u8 rw, func, mode; |
| t_u16 blklen = 0, blknum = 0; |
| int reg = 0; |
| t_u32 pattern_len = 0, total_len = 0; |
| t_u16 cmd_len; |
| gfp_t flag; |
| |
| ENTER(); |
| |
| header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_SD_CMD53_RW); |
| |
| flag = (in_atomic() || irqs_disabled())? GFP_ATOMIC : GFP_KERNEL; |
| data = kzalloc(WOAL_2K_BYTES, flag); |
| if (!data) { |
| PRINTM(MERROR, "Cannot allocate buffer for command!\n"); |
| ret = -EFAULT; |
| goto done; |
| } |
| memcpy(&cmd_len, respbuf + header_len, sizeof(cmd_len)); |
| buf = respbuf + header_len + sizeof(cmd_len); |
| |
| 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) |
| blklen = 1; |
| else |
| 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, |
| respbuf, reg, total_len)) { |
| PRINTM(MERROR, |
| "sdio_readsb: reading memory 0x%x failed\n", |
| reg); |
| goto done; |
| } |
| sdio_release_host(((struct sdio_mmc_card *)priv->phandle-> |
| card)->func); |
| ret = total_len; |
| } else { |
| int pos = 0; |
| pattern_len = cmd_len - 11; |
| if (pattern_len > total_len) |
| pattern_len = total_len; |
| |
| /* 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: |
| kfree(data); |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief Set/Get Port Control mode |
| * @param priv Pointer to moal_private structure |
| * @param respbuf Pointer to response buffer |
| * @param resplen Response buffer length |
| * |
| * @return Number of bytes written, negative for failure. |
| */ |
| static int |
| woal_priv_port_ctrl(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| int header_len = 0, user_data_len = 0; |
| mlan_ioctl_req *req = NULL; |
| mlan_ds_sec_cfg *sec = NULL; |
| int ret = 0, data = 0; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| ENTER(); |
| |
| /* Allocate an IOCTL request buffer */ |
| req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg)); |
| if (req == NULL) { |
| ret = -ENOMEM; |
| goto done; |
| } |
| |
| /* Fill request buffer */ |
| sec = (mlan_ds_sec_cfg *)req->pbuf; |
| sec->sub_command = MLAN_OID_SEC_CFG_PORT_CTRL_ENABLED; |
| req->req_id = MLAN_IOCTL_SEC_CFG; |
| |
| header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_PORT_CTRL); |
| if (strlen(respbuf) == header_len) { |
| /* GET operation */ |
| user_data_len = 0; |
| req->action = MLAN_ACT_GET; |
| } else { |
| /* SET operation */ |
| parse_arguments(respbuf + header_len, &data, |
| sizeof(data) / sizeof(int), &user_data_len); |
| if (user_data_len == 1) { |
| sec->param.port_ctrl_enabled = data; |
| req->action = MLAN_ACT_SET; |
| } else { |
| PRINTM(MERROR, "Invalid number of parameters\n"); |
| ret = -EINVAL; |
| goto done; |
| } |
| } |
| |
| /* Send IOCTL request to MLAN */ |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| goto done; |
| } |
| |
| if (!user_data_len) { |
| memcpy(respbuf, &sec->param.port_ctrl_enabled, sizeof(int)); |
| ret = sizeof(int); |
| } |
| |
| done: |
| if (status != MLAN_STATUS_PENDING) |
| kfree(req); |
| |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief Private IOCTL entry to get the By-passed TX packet from upper layer |
| * @param priv Pointer to moal_private structure |
| * @param respbuf Pointer to response buffer |
| * @param resplen Response buffer length |
| * |
| * @return Number of bytes written, negative for failure. |
| */ |
| static int |
| woal_priv_bypassed_packet(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| int header_len = 0; |
| 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) |
| |
| header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_PB_BYPASS); |
| copyLen = strlen(respbuf) - header_len; |
| 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); |
| |
| memcpy(skb_put(skb, copyLen), respbuf + header_len, copyLen); |
| |
| 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); |
| |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief Control Coalescing status Enable/Disable |
| * @param priv Pointer to moal_private structure |
| * @param respbuf Pointer to response buffer |
| * @param resplen Response buffer length |
| * |
| * @return Number of bytes written, negative for failure. |
| */ |
| static int |
| woal_priv_coalescing_status(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| int header_len = 0, user_data_len = 0; |
| int ret = 0, data = 0; |
| mlan_ds_misc_cfg *pcoal = NULL; |
| mlan_ioctl_req *req = NULL; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| |
| req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); |
| if (req == NULL) { |
| ret = -ENOMEM; |
| goto done; |
| } |
| pcoal = (mlan_ds_misc_cfg *)req->pbuf; |
| |
| header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_COALESCE_STATUS); |
| if (strlen(respbuf) == header_len) { |
| /* GET operation */ |
| user_data_len = 0; |
| req->action = MLAN_ACT_GET; |
| } else { |
| /* SET operation */ |
| parse_arguments(respbuf + header_len, &data, |
| sizeof(data) / sizeof(int), &user_data_len); |
| if (user_data_len == 1) { |
| if (data == 1) |
| pcoal->param.coalescing_status = |
| MLAN_MISC_COALESCING_ENABLE; |
| else |
| pcoal->param.coalescing_status = |
| MLAN_MISC_COALESCING_DISABLE; |
| req->action = MLAN_ACT_SET; |
| } else { |
| PRINTM(MERROR, "Invalid number of parameters\n"); |
| ret = -EINVAL; |
| goto done; |
| } |
| } |
| |
| req->req_id = MLAN_IOCTL_MISC_CFG; |
| pcoal->sub_command = MLAN_OID_MISC_COALESCING_STATUS; |
| |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| goto done; |
| } |
| data = (int)(((mlan_ds_misc_cfg *)req->pbuf)->param.coalescing_status); |
| |
| memcpy(respbuf, &data, sizeof(int)); |
| ret = sizeof(int); |
| |
| done: |
| if (status != MLAN_STATUS_PENDING) |
| kfree(req); |
| LEAVE(); |
| return ret; |
| } |
| |
| #if defined(WIFI_DIRECT_SUPPORT) |
| #if defined(UAP_CFG80211) |
| #if LINUX_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION |
| /** |
| * @brief Set/Get P2P NoA (Notice of Absence) parameters |
| * @param priv Pointer to moal_private structure |
| * @param respbuf Pointer to response buffer |
| * @param resplen Response buffer length |
| * |
| * @return Number of bytes written, negative for failure. |
| */ |
| static int |
| woal_priv_noa_cfg(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| int header_len = 0, user_data_len = 0; |
| int ret = 0, data[7]; |
| mlan_ds_wifi_direct_config noa_cfg; |
| |
| ENTER(); |
| |
| header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_NOA_CFG); |
| memset(&noa_cfg, 0, sizeof(noa_cfg)); |
| if (woal_p2p_config(priv, MLAN_ACT_GET, &noa_cfg) != |
| MLAN_STATUS_SUCCESS) { |
| PRINTM(MERROR, "Could not get P2P noa config\n"); |
| ret = -EINVAL; |
| goto done; |
| } |
| |
| memset(data, 0, sizeof(data)); |
| if (strlen(respbuf) == header_len) { |
| /* GET operation */ |
| memcpy(respbuf, &noa_cfg, sizeof(noa_cfg)); |
| ret = sizeof(noa_cfg); |
| } else { |
| /* SET operation */ |
| parse_arguments(respbuf + header_len, data, ARRAY_SIZE(data), |
| &user_data_len); |
| if (user_data_len > 7) { |
| ret = -EINVAL; |
| goto done; |
| } |
| switch (user_data_len) { |
| case 7: |
| noa_cfg.ct_window = (t_u8)data[6]; |
| case 6: |
| if (data[5] < 0 || data[5] > 1) { |
| PRINTM(MERROR, "Invalid ps enable\n"); |
| ret = -EINVAL; |
| goto done; |
| } |
| noa_cfg.opp_ps_enable = (t_u8)data[5]; |
| noa_cfg.flags |= WIFI_DIRECT_OPP_PS; |
| case 5: |
| noa_cfg.noa_interval = (t_u32)data[4]; |
| case 4: |
| noa_cfg.noa_duration = (t_u32)data[3]; |
| case 3: |
| if (data[2] < 1 || data[2] > 255) { |
| PRINTM(MERROR, |
| "Invalid number of absence intervals\n"); |
| ret = -EINVAL; |
| goto done; |
| } |
| noa_cfg.noa_count = (t_u8)data[2]; |
| case 2: |
| if (data[1] < 0 || data[1] > 255) { |
| PRINTM(MERROR, "Invalid Index\n"); |
| ret = -EINVAL; |
| goto done; |
| } |
| noa_cfg.index = (t_u16)data[1]; |
| case 1: |
| if (data[0] < 0 || data[0] > 1) { |
| PRINTM(MERROR, "Invalid noa enable\n"); |
| ret = -EINVAL; |
| goto done; |
| } |
| noa_cfg.noa_enable = (t_u8)data[0]; |
| noa_cfg.flags |= WIFI_DIRECT_NOA; |
| break; |
| default: |
| break; |
| } |
| woal_p2p_config(priv, MLAN_ACT_SET, &noa_cfg); |
| } |
| done: |
| |
| LEAVE(); |
| return ret; |
| } |
| #endif |
| #endif |
| #endif |
| |
| #if defined(STA_CFG80211) || defined(UAP_CFG80211) |
| #define DEF_NOA_INTERVAL 100 |
| /** |
| ** @brief Set/Get P2P NoA (Notice of Absence) parameters |
| ** @param priv Pointer to moal_private structure |
| ** @param respbuf Pointer to response buffer |
| ** @param resplen Response buffer length |
| ** |
| ** @return Number of bytes written, negative for failure. |
| **/ |
| static int |
| woal_p2p_ps_cfg(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| int user_data_len = 0; |
| int ret = 0, data[2]; |
| u32 duration = priv->phandle->noa_duration; |
| u32 interval = 0; |
| |
| ENTER(); |
| if (strlen(respbuf) > strlen("P2P_PERIODIC_SLEEP")) { |
| memset((char *)data, 0, sizeof(data)); |
| parse_arguments(respbuf + strlen("P2P_PERIODIC_SLEEP") + 1, |
| data, ARRAY_SIZE(data), &user_data_len); |
| } |
| if ((user_data_len != 1) && (user_data_len != 2)) { |
| PRINTM(MERROR, |
| " Invalid parameter number for P2P_PERIODIC_SLEEP"); |
| ret = -EINVAL; |
| goto done; |
| } |
| if (data[0] < DEF_NOA_INTERVAL) |
| interval = DEF_NOA_INTERVAL; |
| else |
| interval = |
| (data[0] + DEF_NOA_INTERVAL - |
| 1) / DEF_NOA_INTERVAL * DEF_NOA_INTERVAL; |
| |
| if (user_data_len == 2) |
| duration = data[1]; |
| if (duration >= interval) { |
| PRINTM(MERROR, |
| " Invalid noa duration/interval! duration=%d interval=%d\n", |
| duration, interval); |
| ret = -EINVAL; |
| goto done; |
| } |
| priv->phandle->noa_interval = interval; |
| priv->phandle->noa_duration = duration; |
| PRINTM(MIOCTL, "configure noa interval=%d, duration=%d\n", |
| priv->phandle->noa_interval, priv->phandle->noa_duration); |
| done: |
| LEAVE(); |
| return ret; |
| } |
| #endif |
| |
| #ifdef WIFI_DIRECT_SUPPORT |
| #if defined(STA_CFG80211) || defined(UAP_CFG80211) |
| /** |
| * @brief Set/Get MIRACAST configuration parameters |
| * |
| * @param priv Pointer to moal_private structure |
| * @param respbuf Pointer to response buffer |
| * @param resplen Response buffer length |
| * |
| * @return Number of bytes written, negative for failure. |
| */ |
| static int |
| woal_priv_miracast_cfg(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| int ret = 0; |
| int user_data_len = 0, header_len = 0, data[3] = { 0, 0, 0 }; |
| |
| ENTER(); |
| |
| if (!priv || !priv->phandle) { |
| PRINTM(MERROR, "priv or handle is null\n"); |
| ret = -EFAULT; |
| goto done; |
| } |
| |
| header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_MIRACAST_CFG); |
| |
| if (strlen(respbuf) == header_len) { |
| /* GET operation */ |
| data[0] = priv->phandle->miracast_mode; |
| data[1] = priv->phandle->miracast_scan_time; |
| data[2] = priv->phandle->scan_chan_gap; |
| |
| memcpy(respbuf, (t_u8 *)data, sizeof(data)); |
| ret = sizeof(data); |
| } else { |
| /* SET operation */ |
| memset(data, 0, sizeof(data)); |
| parse_arguments(respbuf + header_len, data, ARRAY_SIZE(data), |
| &user_data_len); |
| |
| if (user_data_len > 3) { |
| PRINTM(MERROR, "Too many arguments\n"); |
| ret = -EINVAL; |
| goto done; |
| } |
| if (data[0] < 0 || data[0] > 2 || data[1] < 0 || data[2] < 0) { |
| PRINTM(MERROR, "Invalid argument\n"); |
| ret = -EINVAL; |
| goto done; |
| } |
| } |
| |
| if (user_data_len >= 1) |
| priv->phandle->miracast_mode = (t_u8)data[0]; |
| if (user_data_len >= 2) |
| priv->phandle->miracast_scan_time = (t_u16)data[1]; |
| if (user_data_len == 3) |
| priv->phandle->scan_chan_gap = (t_u16)data[2]; |
| |
| done: |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief Configuring scan gap for miracast mode |
| * |
| * @param priv A pointer to moal_private structure |
| * @param respbuf A pointer to response buffer |
| * @param respbuflen Available length of response buffer |
| * |
| * @return 0 --success, otherwise failure |
| */ |
| int |
| woal_set_scan_chan_gap(moal_private *priv, t_u8 *respbuf, int respbuflen) |
| { |
| t_u32 data[2]; |
| int ret = 0; |
| int user_data_len = 0; |
| |
| ENTER(); |
| |
| if (strlen(respbuf) > strlen("SCAN_TIMING")) { |
| memset((char *)data, 0, sizeof(data)); |
| parse_arguments(respbuf + strlen("SCAN_TIMING") + 1, data, |
| ARRAY_SIZE(data), &user_data_len); |
| } |
| |
| if (user_data_len != 2) { |
| PRINTM(MERROR, "Invalid arguments for scan timing\n"); |
| ret = -EINVAL; |
| goto done; |
| } |
| priv->phandle->miracast_scan_time = (t_u16)data[0]; |
| priv->phandle->scan_chan_gap = (t_u16)data[1]; |
| done: |
| LEAVE(); |
| return ret; |
| |
| } |
| #endif |
| #endif |
| |
| /** |
| * @brief Set/Get control to coex RX window size configuration |
| * |
| * @param priv Pointer to moal_private structure |
| * @param respbuf Pointer to response buffer |
| * @param resplen Response buffer length |
| * |
| * @return Number of bytes written, negative for failure. |
| */ |
| static int |
| woal_priv_coex_rx_winsize(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| int ret = 0; |
| int user_data_len = 0, header_len = 0, data; |
| mlan_ioctl_req *req = NULL; |
| mlan_ds_11n_cfg *cfg_11n = NULL; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| |
| if (!priv || !priv->phandle) { |
| PRINTM(MERROR, "priv or handle is null\n"); |
| ret = -EFAULT; |
| goto done; |
| } |
| |
| header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_COEX_RX_WINSIZE); |
| |
| /* 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 */ |
| req->req_id = MLAN_IOCTL_11N_CFG; |
| cfg_11n = (mlan_ds_11n_cfg *)req->pbuf; |
| cfg_11n->sub_command = MLAN_OID_11N_CFG_COEX_RX_WINSIZE; |
| |
| if (strlen(respbuf) == header_len) { |
| /* GET operation */ |
| user_data_len = 0; |
| req->action = MLAN_ACT_GET; |
| } else { |
| /* SET operation */ |
| parse_arguments(respbuf + header_len, &data, |
| sizeof(data) / sizeof(int), &user_data_len); |
| if (user_data_len != 1) { |
| PRINTM(MERROR, "Invalid number of args! %d\n", |
| user_data_len); |
| ret = -EINVAL; |
| goto done; |
| } |
| if ((data != MTRUE) && (data != MFALSE)) { |
| PRINTM(MERROR, |
| "Invalid coex RX window size parameter %d\n", |
| data); |
| ret = -EINVAL; |
| goto done; |
| } |
| cfg_11n->param.coex_rx_winsize = data; |
| req->action = MLAN_ACT_SET; |
| } |
| |
| /* Send IOCTL request to MLAN */ |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| goto done; |
| } |
| |
| if (!user_data_len) { |
| memcpy(respbuf, (t_u8 *)&cfg_11n->param.coex_rx_winsize, |
| sizeof(t_u32)); |
| ret = sizeof(t_u32); |
| } |
| |
| done: |
| if (status != MLAN_STATUS_PENDING) |
| kfree(req); |
| |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief Set/Get control to TX AMPDU configuration on infra link |
| * |
| * @param priv Pointer to moal_private structure |
| * @param respbuf Pointer to response buffer |
| * @param resplen Response buffer length |
| * |
| * @return Number of bytes written, negative for failure. |
| */ |
| static int |
| woal_priv_txaggrctrl(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| int ret = 0; |
| int user_data_len = 0, header_len = 0, data; |
| mlan_ioctl_req *req = NULL; |
| mlan_ds_11n_cfg *cfg_11n = NULL; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| |
| if (!priv || !priv->phandle) { |
| PRINTM(MERROR, "priv or handle is null\n"); |
| ret = -EFAULT; |
| goto done; |
| } |
| |
| header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_TX_AGGR_CTRL); |
| |
| /* 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 */ |
| req->req_id = MLAN_IOCTL_11N_CFG; |
| cfg_11n = (mlan_ds_11n_cfg *)req->pbuf; |
| cfg_11n->sub_command = MLAN_OID_11N_CFG_TX_AGGR_CTRL; |
| |
| if (strlen(respbuf) == header_len) { |
| /* GET operation */ |
| user_data_len = 0; |
| req->action = MLAN_ACT_GET; |
| } else { |
| /* SET operation */ |
| parse_arguments(respbuf + header_len, &data, |
| sizeof(data) / sizeof(int), &user_data_len); |
| if (user_data_len != 1) { |
| PRINTM(MERROR, "Invalid number of args! %d\n", |
| user_data_len); |
| ret = -EINVAL; |
| goto done; |
| } |
| if ((data != MTRUE) && (data != MFALSE)) { |
| PRINTM(MERROR, "Invalid txaggrctrl parameter %d\n", |
| data); |
| ret = -EINVAL; |
| goto done; |
| } |
| cfg_11n->param.txaggrctrl = data; |
| req->action = MLAN_ACT_SET; |
| } |
| |
| /* Send IOCTL request to MLAN */ |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| goto done; |
| } |
| |
| if (!user_data_len) { |
| memcpy(respbuf, (t_u8 *)&cfg_11n->param.txaggrctrl, |
| sizeof(t_u32)); |
| ret = sizeof(t_u32); |
| } |
| |
| done: |
| if (status != MLAN_STATUS_PENDING) |
| kfree(req); |
| |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief Set/Get control to enable/disable auto TDLS |
| * |
| * @param priv Pointer to moal_private structure |
| * @param respbuf Pointer to response buffer |
| * @param resplen Response buffer length |
| * |
| * @return Number of bytes written, negative for failure. |
| */ |
| static int |
| woal_priv_auto_tdls(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) |
| { |
| int ret = 0; |
| int user_data_len = 0, header_len = 0, data; |
| |
| ENTER(); |
| |
| if (!priv || !priv->phandle) { |
| PRINTM(MERROR, "priv or handle is null\n"); |
| ret = -EFAULT; |
| goto done; |
| } |
| |
| header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_AUTO_TDLS); |
| |
| if (strlen(respbuf) == header_len) { |
| /* GET operation */ |
| data = priv->enable_auto_tdls; |
| memcpy(respbuf, (t_u8 *)&data, sizeof(data)); |
| ret = sizeof(data); |
| } else { |
| /* SET operation */ |
| parse_arguments(respbuf + header_len, &data, |
| sizeof(data) / sizeof(int), &user_data_len); |
| if (user_data_len != 1) { |
| PRINTM(MERROR, "Invalid number of args! %d\n", |
| user_data_len); |
| ret = -EINVAL; |
| goto done; |
| } |
| if ((data != MTRUE) && (data != MFALSE)) { |
| PRINTM(MERROR, "Invalid autotdls parameter %d\n", data); |
| ret = -EINVAL; |
| goto done; |
| } |
| priv->enable_auto_tdls = (t_u8)data; |
| } |
| |
| done: |
| 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]; |
| int copy_len = 0; |
| #endif |
| int len = 0; |
| gfp_t flag; |
| char *cmd_buf = NULL; |
| |
| ENTER(); |
| if (!priv || !priv->phandle) { |
| PRINTM(MERROR, "priv or handle is NULL\n"); |
| ret = -EFAULT; |
| goto done; |
| } |
| if (copy_from_user(&priv_cmd, req->ifr_data, |
| sizeof(android_wifi_priv_cmd))) { |
| ret = -EFAULT; |
| goto done; |
| } |
| #define CMD_BUF_LEN 2048 |
| if (priv_cmd.used_len < 0 || priv_cmd.total_len <= 0 || |
| priv_cmd.used_len > priv_cmd.total_len) { |
| PRINTM(MERROR, |
| "Invalid Android priv cmd len. used_len: %d, total_len: %d\n", |
| priv_cmd.used_len, priv_cmd.total_len); |
| ret = -EINVAL; |
| goto done; |
| } |
| if (priv_cmd.total_len + 1 > CMD_BUF_LEN) |
| priv_cmd.total_len = CMD_BUF_LEN - 1; |
| |
| flag = (in_atomic() || irqs_disabled())? GFP_ATOMIC : GFP_KERNEL; |
| buf = kzalloc(CMD_BUF_LEN, flag); |
| if (!buf) { |
| PRINTM(MERROR, "%s: failed to allocate memory\n", __FUNCTION__); |
| ret = -ENOMEM; |
| goto done; |
| } |
| #ifdef USERSPACE_32BIT_OVER_KERNEL_64BIT |
| memcpy(&cmd_buf, &priv_cmd.buf, sizeof(cmd_buf)); |
| #else |
| cmd_buf = priv_cmd.buf; |
| #endif |
| if (copy_from_user(buf, cmd_buf, priv_cmd.total_len)) { |
| ret = -EFAULT; |
| goto done; |
| } |
| buf[CMD_BUF_LEN - 1] = '\0'; |
| |
| PRINTM(MIOCTL, "Android priv cmd: [%s] on [%s]\n", buf, req->ifr_name); |
| |
| if (strncmp(buf, CMD_MARVELL, strlen(CMD_MARVELL)) && |
| woal_check_driver_status(priv->phandle)) { |
| PRINTM(MERROR, "%s fail when driver hang\n", buf); |
| ret = -EFAULT; |
| goto done; |
| } |
| |
| 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; |
| } 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; |
| } 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_HTCAPINFO, |
| strlen(PRIV_CMD_HTCAPINFO)) == 0) { |
| /* Set/Get HT Capability information */ |
| len = woal_setget_priv_htcapinfo(priv, buf, |
| priv_cmd.total_len); |
| goto handled; |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_ADDBAPARA, |
| strlen(PRIV_CMD_ADDBAPARA)) == 0) { |
| /* Set/Get Add BA parameters */ |
| len = woal_setget_priv_addbapara(priv, buf, |
| priv_cmd.total_len); |
| goto handled; |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_AGGRPRIOTBL, |
| strlen(PRIV_CMD_AGGRPRIOTBL)) == 0) { |
| /* Set/Get Aggregation priority table parameters */ |
| len = woal_setget_priv_aggrpriotbl(priv, buf, |
| priv_cmd.total_len); |
| goto handled; |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_ADDBAREJECT, |
| strlen(PRIV_CMD_ADDBAREJECT)) == 0) { |
| /* Set/Get Add BA reject parameters */ |
| len = woal_setget_priv_addbareject(priv, buf, |
| priv_cmd.total_len); |
| goto handled; |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_DELBA, |
| strlen(PRIV_CMD_DELBA)) == 0) { |
| /* Delete selective BA based on parameters */ |
| len = woal_priv_delba(priv, buf, priv_cmd.total_len); |
| goto handled; |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_REJECTADDBAREQ, |
| strlen(PRIV_CMD_REJECTADDBAREQ)) == 0) { |
| /* Set/Get the reject addba requst conditions */ |
| len = woal_priv_rejectaddbareq(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; |
| #ifdef STA_SUPPORT |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_GETLOG, |
| strlen(PRIV_CMD_GETLOG)) == 0) { |
| /* Get wireless stats information */ |
| len = woal_get_priv_getlog(priv, buf, |
| priv_cmd.total_len); |
| goto handled; |
| #endif |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_CUSTOMIE, |
| strlen(PRIV_CMD_CUSTOMIE)) == 0) { |
| /* Custom IE configuration */ |
| len = woal_priv_customie(priv, buf, priv_cmd.total_len); |
| goto handled; |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_ESUPPMODE, |
| strlen(PRIV_CMD_ESUPPMODE)) == 0) { |
| /* Esupplicant mode configuration */ |
| len = woal_setget_priv_esuppmode(priv, buf, |
| priv_cmd.total_len); |
| goto handled; |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_PASSPHRASE, |
| strlen(PRIV_CMD_PASSPHRASE)) == 0) { |
| /* Esupplicant passphrase configuration */ |
| len = woal_setget_priv_passphrase(priv, buf, |
| priv_cmd.total_len); |
| goto handled; |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_DEAUTH, |
| strlen(PRIV_CMD_DEAUTH)) == 0) { |
| /* Deauth */ |
| len = woal_priv_deauth(priv, buf, priv_cmd.total_len); |
| goto handled; |
| #ifdef UAP_SUPPORT |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_AP_DEAUTH, |
| strlen(PRIV_CMD_AP_DEAUTH)) == 0) { |
| /* AP Deauth */ |
| len = woal_priv_ap_deauth(priv, buf, |
| priv_cmd.total_len); |
| goto handled; |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_GET_STA_LIST, |
| strlen(PRIV_CMD_GET_STA_LIST)) == 0) { |
| /* Get STA list */ |
| len = woal_priv_get_sta_list(priv, buf, |
| priv_cmd.total_len); |
| goto handled; |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_BSS_CONFIG, |
| strlen(PRIV_CMD_BSS_CONFIG)) == 0) { |
| /* BSS config */ |
| len = woal_priv_bss_config(priv, buf, |
| priv_cmd.total_len); |
| goto handled; |
| #endif |
| #if defined(WIFI_DIRECT_SUPPORT) |
| #if defined(STA_SUPPORT) && defined(UAP_SUPPORT) |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_BSSROLE, |
| strlen(PRIV_CMD_BSSROLE)) == 0) { |
| /* BSS Role */ |
| len = woal_priv_bssrole(priv, buf, |
| (t_u32)priv_cmd.total_len); |
| goto handled; |
| #endif |
| #endif |
| #ifdef STA_SUPPORT |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_SETUSERSCAN, |
| strlen(PRIV_CMD_SETUSERSCAN)) == 0) { |
| /* Set user scan */ |
| len = woal_priv_setuserscan(priv, buf, |
| priv_cmd.total_len); |
| goto handled; |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_GETSCANTABLE, |
| strlen(PRIV_CMD_GETSCANTABLE)) == 0) { |
| /* Get scan table */ |
| len = woal_priv_getscantable(priv, buf, |
| priv_cmd.total_len); |
| goto handled; |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_EXTCAPCFG, |
| strlen(PRIV_CMD_EXTCAPCFG)) == 0) { |
| /* Extended capabilities configure */ |
| len = woal_priv_extcapcfg(priv, buf, |
| (t_u32)priv_cmd.total_len); |
| goto handled; |
| #endif |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_DEEPSLEEP, |
| strlen(PRIV_CMD_DEEPSLEEP)) == 0) { |
| /* Deep sleep */ |
| len = woal_priv_setgetdeepsleep(priv, buf, |
| priv_cmd.total_len); |
| goto handled; |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_IPADDR, |
| strlen(PRIV_CMD_IPADDR)) == 0) { |
| /* IP address */ |
| len = woal_priv_setgetipaddr(priv, buf, |
| (t_u32)priv_cmd.total_len); |
| goto handled; |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_WPSSESSION, |
| strlen(PRIV_CMD_WPSSESSION)) == 0) { |
| /* WPS Session */ |
| len = woal_priv_setwpssession(priv, buf, |
| priv_cmd.total_len); |
| goto handled; |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_OTPUSERDATA, |
| strlen(PRIV_CMD_OTPUSERDATA)) == 0) { |
| /* OTP user data */ |
| len = woal_priv_otpuserdata(priv, buf, |
| priv_cmd.total_len); |
| goto handled; |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_COUNTRYCODE, |
| strlen(PRIV_CMD_COUNTRYCODE)) == 0) { |
| /* Country code */ |
| len = woal_priv_set_get_countrycode(priv, buf, |
| priv_cmd.total_len); |
| goto handled; |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_TCPACKENH, |
| strlen(PRIV_CMD_TCPACKENH)) == 0) { |
| /* TCP ack enhancement */ |
| len = woal_priv_setgettcpackenh(priv, buf, |
| priv_cmd.total_len); |
| goto handled; |
| #ifdef REASSOCIATION |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_ASSOCESSID, |
| strlen(PRIV_CMD_ASSOCESSID)) == 0) { |
| /* Associate to essid */ |
| len = woal_priv_assocessid(priv, buf, |
| priv_cmd.total_len); |
| goto handled; |
| #endif |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_WAKEUPREASON, |
| strlen(PRIV_CMD_WAKEUPREASON)) == 0) { |
| /* wakeup reason */ |
| len = woal_priv_getwakeupreason(priv, buf, |
| priv_cmd.total_len); |
| goto handled; |
| #ifdef STA_SUPPORT |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_LISTENINTERVAL, |
| strlen(PRIV_CMD_LISTENINTERVAL)) == 0) { |
| /* Listen Interval */ |
| len = woal_priv_set_get_listeninterval(priv, buf, |
| priv_cmd. |
| total_len); |
| goto handled; |
| #endif |
| #ifdef DEBUG_LEVEL1 |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_DRVDBG, |
| strlen(PRIV_CMD_DRVDBG)) == 0) { |
| /* Driver debug bit mask */ |
| len = woal_priv_set_get_drvdbg(priv, buf, |
| priv_cmd.total_len); |
| goto handled; |
| #endif |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_HSCFG, |
| strlen(PRIV_CMD_HSCFG)) == 0) { |
| /* HS configuration */ |
| len = woal_priv_hscfg(priv, buf, priv_cmd.total_len, |
| MTRUE); |
| goto handled; |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_HSSETPARA, |
| strlen(PRIV_CMD_HSSETPARA)) == 0) { |
| /* Set HS parameter */ |
| len = woal_priv_hssetpara(priv, buf, |
| priv_cmd.total_len); |
| goto handled; |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_SCANCFG, |
| strlen(PRIV_CMD_SCANCFG)) == 0) { |
| /* Scan configuration */ |
| len = woal_priv_set_get_scancfg(priv, buf, |
| priv_cmd.total_len); |
| goto handled; |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_SET_BSS_MODE, |
| strlen(PRIV_CMD_SET_BSS_MODE)) == 0) { |
| /* Set bss mode */ |
| len = woal_priv_set_bss_mode(priv, buf, |
| priv_cmd.total_len); |
| goto handled; |
| #ifdef STA_SUPPORT |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_SET_AP, |
| strlen(PRIV_CMD_SET_AP)) == 0) { |
| /* Set AP */ |
| len = woal_priv_set_ap(priv, buf, priv_cmd.total_len); |
| goto handled; |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_SET_POWER, |
| strlen(PRIV_CMD_SET_POWER)) == 0) { |
| /* Set power management parameters */ |
| len = woal_priv_set_power(priv, buf, |
| priv_cmd.total_len); |
| goto handled; |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_SET_ESSID, |
| strlen(PRIV_CMD_SET_ESSID)) == 0) { |
| /* Set essid */ |
| len = woal_priv_set_essid(priv, buf, |
| priv_cmd.total_len); |
| goto handled; |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_SET_AUTH, |
| strlen(PRIV_CMD_SET_AUTH)) == 0) { |
| /* Set authentication mode parameters */ |
| len = woal_priv_set_auth(priv, buf, priv_cmd.total_len); |
| goto handled; |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_GET_AP, |
| strlen(PRIV_CMD_GET_AP)) == 0) { |
| /* Get AP */ |
| len = woal_priv_get_ap(priv, buf, priv_cmd.total_len); |
| goto handled; |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_GET_POWER, |
| strlen(PRIV_CMD_GET_POWER)) == 0) { |
| /* Get power management parameters */ |
| len = woal_priv_get_power(priv, buf, |
| priv_cmd.total_len); |
| goto handled; |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_PSMODE, |
| strlen(PRIV_CMD_PSMODE)) == 0) { |
| /* Set/Get PS mode */ |
| len = woal_priv_set_get_psmode(priv, buf, |
| priv_cmd.total_len); |
| goto handled; |
| #endif |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_WARMRESET, |
| strlen(PRIV_CMD_WARMRESET)) == 0) { |
| /* Performs warm reset */ |
| len = woal_priv_warmreset(priv, buf, |
| priv_cmd.total_len); |
| goto handled; |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_TXPOWERCFG, |
| strlen(PRIV_CMD_TXPOWERCFG)) == 0) { |
| /* TX power configurations */ |
| len = woal_priv_txpowercfg(priv, buf, |
| priv_cmd.total_len); |
| goto handled; |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_PSCFG, |
| strlen(PRIV_CMD_PSCFG)) == 0) { |
| /* PS configurations */ |
| len = woal_priv_pscfg(priv, buf, priv_cmd.total_len); |
| goto handled; |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_SLEEPPD, |
| strlen(PRIV_CMD_SLEEPPD)) == 0) { |
| /* Sleep period */ |
| len = woal_priv_sleeppd(priv, buf, priv_cmd.total_len); |
| goto handled; |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_TXCONTROL, |
| strlen(PRIV_CMD_TXCONTROL)) == 0) { |
| /* Tx control */ |
| len = woal_priv_txcontrol(priv, buf, |
| priv_cmd.total_len); |
| goto handled; |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_REGRDWR, |
| strlen(PRIV_CMD_REGRDWR)) == 0) { |
| /* Register Read/Write */ |
| len = woal_priv_regrdwr(priv, buf, priv_cmd.total_len); |
| goto handled; |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_RDEEPROM, |
| strlen(PRIV_CMD_RDEEPROM)) == 0) { |
| /* Read the EEPROM contents of the card */ |
| len = woal_priv_rdeeprom(priv, buf, priv_cmd.total_len); |
| goto handled; |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_MEMRDWR, |
| strlen(PRIV_CMD_MEMRDWR)) == 0) { |
| /* Memory Read/Write */ |
| len = woal_priv_memrdwr(priv, buf, priv_cmd.total_len); |
| goto handled; |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_SDCMD52RW, |
| strlen(PRIV_CMD_SDCMD52RW)) == 0) { |
| /* Cmd52 read/write register */ |
| len = woal_priv_sdcmd52rw(priv, buf, |
| priv_cmd.total_len); |
| goto handled; |
| #ifdef STA_SUPPORT |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_ARPFILTER, |
| strlen(PRIV_CMD_ARPFILTER)) == 0) { |
| /* ARPFilter Configuration */ |
| len = woal_priv_arpfilter(priv, buf, |
| priv_cmd.total_len); |
| goto handled; |
| #endif |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_HOTSPOTCFG, |
| strlen(PRIV_CMD_HOTSPOTCFG)) == 0) { |
| /* Hotspot CFG */ |
| len = woal_priv_hotspotcfg(priv, buf, |
| priv_cmd.total_len); |
| goto handled; |
| #ifdef RX_PACKET_COALESCE |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_RX_COAL_CFG, |
| strlen(PRIV_CMD_RX_COAL_CFG)) == 0) { |
| /* RX packet coalescing Configuration */ |
| len = woal_priv_rx_pkt_coalesce_cfg(priv, buf, |
| priv_cmd.total_len); |
| goto handled; |
| #endif |
| |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_MGMT_FRAME_CTRL, |
| strlen(PRIV_CMD_MGMT_FRAME_CTRL)) == 0) { |
| /* Mgmt Frame Passthrough Ctrl */ |
| len = woal_priv_mgmt_frame_passthru_ctrl(priv, buf, |
| priv_cmd. |
| total_len); |
| goto handled; |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_QCONFIG, |
| strlen(PRIV_CMD_QCONFIG)) == 0) { |
| /* Queue config */ |
| len = woal_priv_qconfig(priv, buf, priv_cmd.total_len); |
| goto handled; |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_ADDTS, |
| strlen(PRIV_CMD_ADDTS)) == 0) { |
| /* Send an ADDTS TSPEC */ |
| len = woal_priv_wmm_addts_req_ioctl(priv, buf, |
| priv_cmd.total_len); |
| goto handled; |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_DELTS, |
| strlen(PRIV_CMD_DELTS)) == 0) { |
| /* Send a DELTS TSPE */ |
| len = woal_priv_wmm_delts_req_ioctl(priv, buf, |
| priv_cmd.total_len); |
| goto handled; |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_QSTATUS, |
| strlen(PRIV_CMD_QSTATUS)) == 0) { |
| /* Get the status of the WMM queues */ |
| len = woal_priv_wmm_queue_status_ioctl(priv, buf, |
| priv_cmd. |
| total_len); |
| goto handled; |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_TS_STATUS, |
| strlen(PRIV_CMD_TS_STATUS)) == 0) { |
| /* Get the status of the WMM Traffic Streams */ |
| len = woal_priv_wmm_ts_status_ioctl(priv, buf, |
| priv_cmd.total_len); |
| goto handled; |
| #ifdef STA_SUPPORT |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_QOS_CFG, |
| strlen(PRIV_CMD_QOS_CFG)) == 0) { |
| t_u32 action = MLAN_ACT_GET; |
| if (strlen(buf) == |
| strlen(CMD_MARVELL) + strlen(PRIV_CMD_QOS_CFG)) { |
| pdata = buf; /* GET operation */ |
| } else { |
| pdata = buf + strlen(CMD_MARVELL) + |
| strlen(PRIV_CMD_QOS_CFG); |
| action = MLAN_ACT_SET; /* SET operation */ |
| } |
| if (MLAN_STATUS_SUCCESS != |
| woal_priv_qos_cfg(priv, action, pdata)) { |
| ret = -EFAULT; |
| goto done; |
| } |
| if (action == MLAN_ACT_GET) |
| len = sizeof(t_u8); |
| goto handled; |
| #endif |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_MAC_CTRL, |
| strlen(PRIV_CMD_MAC_CTRL)) == 0) { |
| /* MAC CTRL */ |
| len = woal_priv_macctrl(priv, buf, priv_cmd.total_len); |
| goto handled; |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_GETWAP, |
| strlen(PRIV_CMD_GETWAP)) == 0) { |
| /* Get WAP */ |
| len = woal_priv_getwap(priv, buf, priv_cmd.total_len); |
| goto handled; |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_REGION_CODE, |
| strlen(PRIV_CMD_REGION_CODE)) == 0) { |
| /* Region Code */ |
| len = woal_priv_region_code(priv, buf, |
| priv_cmd.total_len); |
| goto handled; |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_MULTI_CHAN_CFG, |
| strlen(PRIV_CMD_MULTI_CHAN_CFG)) == 0) { |
| /* Channel time and buffer weight configuration */ |
| len = woal_priv_multi_chan_config(priv, buf, |
| priv_cmd.total_len); |
| goto handled; |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), |
| PRIV_CMD_MULTI_CHAN_POLICY, |
| strlen(PRIV_CMD_MULTI_CHAN_POLICY)) == 0) { |
| /* Multi-channel Policy enable/disable */ |
| len = woal_priv_multi_chan_policy(priv, buf, |
| priv_cmd.total_len); |
| goto handled; |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_FWMACADDR, |
| strlen(PRIV_CMD_FWMACADDR)) == 0) { |
| /* Set FW MAC address */ |
| len = woal_priv_fwmacaddr(priv, buf, |
| priv_cmd.total_len); |
| goto handled; |
| #if defined(WIFI_DIRECT_SUPPORT) |
| #ifdef STA_CFG80211 |
| #if LINUX_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_OFFCHANNEL, |
| strlen(PRIV_CMD_OFFCHANNEL)) == 0) { |
| if (IS_STA_CFG80211(cfg80211_wext)) { |
| /* Set offchannel */ |
| len = woal_priv_offchannel(priv, buf, |
| priv_cmd.total_len); |
| } else |
| len = sprintf(buf, |
| "CFG80211 is not enabled\n") + 1; |
| goto handled; |
| #endif |
| #endif |
| #endif |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_VEREXT, |
| strlen(PRIV_CMD_VEREXT)) == 0) { |
| /* Get Extended version */ |
| len = woal_priv_get_driver_verext(priv, buf, |
| priv_cmd.total_len); |
| goto handled; |
| #if defined(STA_SUPPORT) |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_RADIO_CTRL, |
| strlen(PRIV_CMD_RADIO_CTRL)) == 0) { |
| /* Set/Get radio */ |
| len = woal_priv_radio_ctrl(priv, buf, |
| priv_cmd.total_len); |
| goto handled; |
| #endif |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_WMM_CFG, |
| strlen(PRIV_CMD_WMM_CFG)) == 0) { |
| /* Implement WMM enable command */ |
| len = woal_priv_wmm_cfg(priv, buf, priv_cmd.total_len); |
| goto handled; |
| #if defined(STA_SUPPORT) |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_11D_CFG, |
| strlen(PRIV_CMD_11D_CFG)) == 0) { |
| /* Implement 802.11D enable command */ |
| len = woal_priv_11d_cfg(priv, buf, priv_cmd.total_len); |
| goto handled; |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_11D_CLR_TBL, |
| strlen(PRIV_CMD_11D_CLR_TBL)) == 0) { |
| /* Implement 802.11D clear chan table command */ |
| len = woal_priv_11d_clr_chan_tbl(priv, buf, |
| priv_cmd.total_len); |
| goto handled; |
| #endif |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_WWS_CFG, |
| strlen(PRIV_CMD_WWS_CFG)) == 0) { |
| /* Set/Get WWS configuration */ |
| len = woal_priv_wws_cfg(priv, buf, priv_cmd.total_len); |
| goto handled; |
| #if defined(REASSOCIATION) |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_REASSOCTRL, |
| strlen(PRIV_CMD_REASSOCTRL)) == 0) { |
| /* Set/Get reassociation settings */ |
| len = woal_priv_set_get_reassoc(priv, buf, |
| priv_cmd.total_len); |
| goto handled; |
| #endif |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_TXBUF_CFG, |
| strlen(PRIV_CMD_TXBUF_CFG)) == 0) { |
| /* Get Transmit buffer size */ |
| len = woal_priv_txbuf_cfg(priv, buf, |
| priv_cmd.total_len); |
| goto handled; |
| #ifdef STA_SUPPORT |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_AUTH_TYPE, |
| strlen(PRIV_CMD_AUTH_TYPE)) == 0) { |
| /* Set/Get auth type */ |
| len = woal_priv_auth_type(priv, buf, |
| priv_cmd.total_len); |
| goto handled; |
| #endif |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_POWER_CONS, |
| strlen(PRIV_CMD_POWER_CONS)) == 0) { |
| /* Set/get user provisioned local power constraint */ |
| len = woal_priv_11h_local_pwr_constraint(priv, buf, |
| priv_cmd. |
| total_len); |
| goto handled; |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_THERMAL, |
| strlen(PRIV_CMD_THERMAL)) == 0) { |
| /* Get thermal reading */ |
| len = woal_priv_thermal(priv, buf, priv_cmd.total_len); |
| goto handled; |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_BCN_INTERVAL, |
| strlen(PRIV_CMD_BCN_INTERVAL)) == 0) { |
| /* Set/Get beacon interval */ |
| len = woal_priv_beacon_interval(priv, buf, |
| priv_cmd.total_len); |
| goto handled; |
| #ifdef STA_SUPPORT |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_GET_SIGNAL, |
| strlen(PRIV_CMD_GET_SIGNAL)) == 0) { |
| /* Get signal */ |
| len = woal_priv_get_signal(priv, buf, |
| priv_cmd.total_len); |
| goto handled; |
| #endif |
| #if defined(STA_SUPPORT) |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_PMFCFG, |
| strlen(PRIV_CMD_PMFCFG)) == 0) { |
| /* Configure PMF */ |
| len = woal_priv_set_get_pmfcfg(priv, buf, |
| priv_cmd.total_len); |
| goto handled; |
| #endif |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_INACTIVITYTO, |
| strlen(PRIV_CMD_INACTIVITYTO)) == 0) { |
| /* Get/Set inactivity timeout extend */ |
| len = woal_priv_inactivity_timeout_ext(priv, buf, |
| priv_cmd. |
| total_len); |
| goto handled; |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_AMSDU_AGGR_CTRL, |
| strlen(PRIV_CMD_AMSDU_AGGR_CTRL)) == 0) { |
| /* Enable/Disable amsdu_aggr_ctrl */ |
| len = woal_priv_11n_amsdu_aggr_ctrl(priv, buf, |
| priv_cmd.total_len); |
| goto handled; |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_TX_BF_CAP, |
| strlen(PRIV_CMD_TX_BF_CAP)) == 0) { |
| /* Set/Get Transmit beamforming capabilities */ |
| len = woal_priv_tx_bf_cap_ioctl(priv, buf, |
| priv_cmd.total_len); |
| goto handled; |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_SDIO_CLOCK, |
| strlen(PRIV_CMD_SDIO_CLOCK)) == 0) { |
| /* Turn on/off the sdio clock */ |
| len = woal_priv_sdio_clock_ioctl(priv, buf, |
| priv_cmd.total_len); |
| goto handled; |
| #if defined(SDIO_MULTI_PORT_TX_AGGR) || defined(SDIO_MULTI_PORT_RX_AGGR) |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_MPA_CTRL, |
| strlen(PRIV_CMD_MPA_CTRL)) == 0) { |
| /* Set SDIO Multi-point aggregation control parameters */ |
| len = woal_priv_sdio_mpa_ctrl(priv, buf, |
| priv_cmd.total_len); |
| goto handled; |
| #endif |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_SLEEP_PARAMS, |
| strlen(PRIV_CMD_SLEEP_PARAMS)) == 0) { |
| /* Configure sleep parameters */ |
| len = woal_priv_sleep_params_ioctl(priv, buf, |
| priv_cmd.total_len); |
| goto handled; |
| #if defined(DFS_TESTING_SUPPORT) |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_DFS_TESTING, |
| strlen(PRIV_CMD_DFS_TESTING)) == 0) { |
| /* Set/Get DFS Testing settings */ |
| len = woal_priv_dfs_testing(priv, buf, |
| priv_cmd.total_len); |
| goto handled; |
| #endif |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_CFP_CODE, |
| strlen(PRIV_CMD_CFP_CODE)) == 0) { |
| /* Set/Get CFP table codes */ |
| len = woal_priv_cfp_code(priv, buf, priv_cmd.total_len); |
| goto handled; |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_ANT_CFG, |
| strlen(PRIV_CMD_ANT_CFG)) == 0) { |
| /* Set/Get Tx/Rx antenna */ |
| len = woal_priv_set_get_tx_rx_ant(priv, buf, |
| priv_cmd.total_len); |
| goto handled; |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_SYSCLOCK, |
| strlen(PRIV_CMD_SYSCLOCK)) == 0) { |
| /* Get/Set system clock */ |
| len = woal_priv_sysclock(priv, buf, priv_cmd.total_len); |
| goto handled; |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_ADHOC_AES, |
| strlen(PRIV_CMD_ADHOC_AES)) == 0) { |
| /* Adhoc AES control */ |
| len = woal_priv_adhoc_aes(priv, buf, |
| priv_cmd.total_len); |
| goto handled; |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_GET_KEY, |
| strlen(PRIV_CMD_GET_KEY)) == 0) { |
| /* Get GTK/PTK */ |
| len = woal_priv_get_key(priv, buf, priv_cmd.total_len); |
| goto handled; |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_ASSOCIATE, |
| strlen(PRIV_CMD_ASSOCIATE)) == 0) { |
| /* Associate to a specific indexed entry in the |
| ScanTable */ |
| len = woal_priv_associate_ssid_bssid(priv, buf, |
| priv_cmd. |
| total_len); |
| goto handled; |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_SD_CMD53_RW, |
| strlen(PRIV_CMD_SD_CMD53_RW)) == 0) { |
| /* Cmd53 read/write register */ |
| len = woal_priv_cmd53rdwr(priv, buf, |
| priv_cmd.total_len); |
| goto handled; |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_PORT_CTRL, |
| strlen(PRIV_CMD_PORT_CTRL)) == 0) { |
| /* Set/Get Port Control mode */ |
| len = woal_priv_port_ctrl(priv, buf, |
| priv_cmd.total_len); |
| goto handled; |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_PB_BYPASS, |
| strlen(PRIV_CMD_PB_BYPASS)) == 0) { |
| /* Private IOCTL entry to get the By-passed TX packet |
| from upper layer */ |
| len = woal_priv_bypassed_packet(priv, buf, |
| priv_cmd.total_len); |
| goto handled; |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_COALESCE_STATUS, |
| strlen(PRIV_CMD_COALESCE_STATUS)) == 0) { |
| /* Control Coalescing status Enable/Disable */ |
| len = woal_priv_coalescing_status(priv, buf, |
| priv_cmd.total_len); |
| goto handled; |
| #if defined(WIFI_DIRECT_SUPPORT) |
| #if defined(UAP_CFG80211) |
| #if LINUX_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_NOA_CFG, |
| strlen(PRIV_CMD_NOA_CFG)) == 0) { |
| /* Set/Get P2P NoA (Notice of Absence) parameters */ |
| len = woal_priv_noa_cfg(priv, buf, priv_cmd.total_len); |
| goto handled; |
| #endif |
| #endif |
| #endif |
| #ifdef WIFI_DIRECT_SUPPORT |
| #if defined(STA_CFG80211) || defined(UAP_CFG80211) |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_MIRACAST_CFG, |
| strlen(PRIV_CMD_MIRACAST_CFG)) == 0) { |
| /* Set/Get MIRACAST configuration parameters */ |
| len = woal_priv_miracast_cfg(priv, buf, |
| priv_cmd.total_len); |
| goto handled; |
| #endif |
| #endif |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_COEX_RX_WINSIZE, |
| strlen(PRIV_CMD_COEX_RX_WINSIZE)) == 0) { |
| /* Set/Get control to coex RX window size */ |
| len = woal_priv_coex_rx_winsize(priv, buf, |
| priv_cmd.total_len); |
| goto handled; |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_TX_AGGR_CTRL, |
| strlen(PRIV_CMD_TX_AGGR_CTRL)) == 0) { |
| /* Set/Get control to TX AMPDU on infra link */ |
| len = woal_priv_txaggrctrl(priv, buf, |
| priv_cmd.total_len); |
| goto handled; |
| } else if (strnicmp |
| (buf + strlen(CMD_MARVELL), PRIV_CMD_AUTO_TDLS, |
| strlen(PRIV_CMD_AUTO_TDLS)) == 0) { |
| /* Set/Get control to enable/disable auto TDLS */ |
| len = woal_priv_auto_tdls(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, "RSSILOW-THRESHOLD", strlen("RSSILOW-THRESHOLD")) == 0) { |
| pdata = buf + strlen("RSSILOW-THRESHOLD") + 1; |
| if (MLAN_STATUS_SUCCESS != |
| woal_set_rssi_low_threshold(priv, pdata, MOAL_IOCTL_WAIT)) { |
| ret = -EFAULT; |
| goto done; |
| } |
| len = sprintf(buf, "OK\n") + 1; |
| } else if (strncmp(buf, "SCAN-CFG", strlen("SCAN-CFG")) == 0) { |
| PRINTM(MIOCTL, "Set SCAN CFG\n"); |
| if (MLAN_STATUS_SUCCESS != |
| woal_set_scan_cfg(priv, buf, priv_cmd.total_len)) { |
| ret = -EFAULT; |
| goto done; |
| } |
| len = sprintf(buf, "OK\n") + 1; |
| } else 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, "%.32s 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 (!hw_test) { |
| if (MLAN_STATUS_SUCCESS != |
| woal_set_powermode(priv, pdata)) { |
| ret = -EFAULT; |
| goto done; |
| } |
| } |
| len = sprintf(buf, "OK\n") + 1; |
| } else if (strncmp(buf, "SETROAMING", strlen("SETROAMING")) == 0) { |
| pdata = buf + strlen("SETROAMING") + 1; |
| #ifdef STA_CFG80211 |
| if (*pdata == '1') { |
| priv->roaming_enabled = MTRUE; |
| PRINTM(MIOCTL, "Roaming enabled\n"); |
| } else if (*pdata == '0') { |
| priv->roaming_enabled = MFALSE; |
| PRINTM(MIOCTL, "Roaming disabled\n"); |
| } |
| #endif |
| len = sprintf(buf, "OK\n") + 1; |
| } else if (strncmp(buf, "ROAM", strlen("ROAM")) == 0) { |
| pdata = buf + strlen("ROAM") + 1; |
| #ifdef STA_CFG80211 |
| if (*pdata == '1') { |
| priv->roaming_enabled = MTRUE; |
| PRINTM(MIOCTL, "Roaming enabled\n"); |
| } else if (*pdata == '0') { |
| priv->roaming_enabled = MFALSE; |
| PRINTM(MIOCTL, "Roaming disabled\n"); |
| } |
| #endif |
| len = sprintf(buf, "OK\n") + 1; |
| } else if (strncmp(buf, "COUNTRY", strlen("COUNTRY")) == 0) { |
| copy_len = strlen(buf) - strlen("COUNTRY") - 1; |
| if (copy_len > COUNTRY_CODE_LEN || copy_len <= 0) { |
| PRINTM(MERROR, "Invalid country length\n"); |
| ret = -EFAULT; |
| goto done; |
| } |
| memset(country_code, 0, sizeof(country_code)); |
| memcpy(country_code, buf + strlen("COUNTRY") + 1, copy_len); |
| PRINTM(MIOCTL, "Set COUNTRY %s\n", country_code); |
| if (MLAN_STATUS_SUCCESS != |
| woal_request_country_power_table(priv, country_code)) { |
| ret = -EFAULT; |
| goto done; |
| } |
| #ifdef STA_CFG80211 |
| if (IS_STA_CFG80211(cfg80211_wext)) { |
| PRINTM(MIOCTL, "Notify country code=%s\n", |
| country_code); |
| regulatory_hint(priv->wdev->wiphy, country_code); |
| len = sprintf(buf, "OK\n") + 1; |
| goto done; |
| } |
| #endif |
| 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) { |
| ret = woal_uap_bss_ctrl(priv, MOAL_IOCTL_WAIT, UAP_BSS_START); |
| if (ret) |
| goto done; |
| len = sprintf(buf, "OK\n") + 1; |
| } else if (strncmp(buf, "AP_BSS_STOP", strlen("AP_BSS_STOP")) == 0) { |
| ret = woal_uap_bss_ctrl(priv, MOAL_IOCTL_WAIT, UAP_BSS_STOP); |
| if (ret) |
| goto done; |
| len = sprintf(buf, "OK\n") + 1; |
| } else if (strncmp(buf, "AP_SET_CFG", strlen("AP_SET_CFG")) == 0) { |
| if (priv_cmd.total_len <= strlen("AP_SET_CFG") + 1) |
| goto done; |
| pdata = buf + strlen("AP_SET_CFG") + 1; |
| ret = woal_uap_set_ap_cfg(priv, pdata, |
| priv_cmd.total_len - |
| strlen("AP_SET_CFG") - 1); |
| if (ret) |
| 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, "SETSUSPENDMODE", strlen("SETSUSPENDMODE")) == |
| 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; |
| } |
| #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 (priv->bg_scan_start && !priv->scan_cfg.rssi_threshold) { |
| if (MLAN_STATUS_SUCCESS != |
| woal_stop_bg_scan(priv, MOAL_NO_WAIT)) { |
| 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 |
| ret = woal_set_rxfilter(priv, MTRUE); |
| if (ret) |
| goto done; |
| #endif |
| len = sprintf(buf, "OK\n") + 1; |
| } else if (strncmp(buf, "RXFILTER-STOP", strlen("RXFILTER-STOP")) == 0) { |
| #ifdef MEF_CFG_RX_FILTER |
| ret = woal_set_rxfilter(priv, MFALSE); |
| if (ret) |
| goto done; |
| #endif |
| len = sprintf(buf, "OK\n") + 1; |
| } |
| #ifdef STA_CFG80211 |
| else if (strncmp(buf, "GET_EVENT", strlen("GET_EVENT")) == 0) { |
| if (IS_STA_CFG80211(cfg80211_wext)) { |
| if (priv->last_event & EVENT_BG_SCAN_REPORT) |
| woal_inform_bss_from_scan_result(priv, NULL, |
| MOAL_IOCTL_WAIT); |
| } |
| len = sprintf(buf, "EVENT=%d\n", priv->last_event) + 1; |
| priv->last_event = 0; |
| } else if (strncmp(buf, "GET_802_11W", strlen("GET_802_11W")) == 0) { |
| len = sprintf(buf, "802_11W=ENABLED\n") + 1; |
| } |
| #endif /* STA_CFG80211 */ |
| 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; |
| #ifdef STA_SUPPORT |
| if (MLAN_STATUS_SUCCESS != |
| woal_priv_qos_cfg(priv, MLAN_ACT_SET, pdata)) { |
| ret = -EFAULT; |
| goto done; |
| } |
| #endif |
| 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 LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0) |
| 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 |
| #endif |
| len = sprintf(buf, "OK\n") + 1; |
| } |
| #endif |
| else if (strncmp(buf, "P2P_DEV_ADDR", strlen("P2P_DEV_ADDR")) == 0) { |
| memset(buf, 0x0, (size_t) 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, (size_t) priv_cmd.total_len); |
| *buf = 0; |
| len = 1; |
| } else if (strnicmp(buf, "MIRACAST", strlen("MIRACAST")) == 0) { |
| pdata = buf + strlen("MIRACAST"); |
| /* Android cmd format: "MIRACAST 0" -- disabled "MIRACAST 1" -- |
| operating as source "MIRACAST 2" -- operating as sink */ |
| #ifdef WIFI_DIRECT_SUPPORT |
| #if defined(STA_CFG80211) || defined(UAP_CFG80211) |
| if (MLAN_STATUS_SUCCESS != |
| woal_set_miracast_mode(priv, (t_u8 *)pdata, |
| priv_cmd.used_len - |
| strlen("MIRACAST"))) { |
| ret = -EFAULT; |
| goto done; |
| } |
| #endif |
| #endif |
| len = sprintf(buf, "OK\n") + 1; |
| } else if (strnicmp(buf, "SCAN_TIMING", strlen("SCAN_TIMING")) == 0) { |
| #ifdef WIFI_DIRECT_SUPPORT |
| #if defined(STA_CFG80211) || defined(UAP_CFG80211) |
| if (MLAN_STATUS_SUCCESS != |
| woal_set_scan_chan_gap(priv, buf, priv_cmd.total_len)) { |
| ret = -EFAULT; |
| goto done; |
| } |
| #endif |
| #endif |
| len = sprintf(buf, "OK\n") + 1; |
| } else if (strnicmp(buf, "BA_WSIZE_RX", strlen("BA_WSIZE_RX")) == 0) { |
| pdata = buf + strlen("BA_WSIZE_RX") + 1; |
| len = priv_cmd.total_len - strlen("BA_WSIZE_RX") - 1; |
| if (MLAN_STATUS_SUCCESS != |
| woal_set_rx_ba_winsize(priv, pdata, len)) { |
| ret = -EFAULT; |
| goto done; |
| } |
| len = sprintf(buf, "OK\n") + 1; |
| } else if (strnicmp(buf, "BA_WSIZE_TX", strlen("BA_WSIZE_TX")) == 0) { |
| pdata = buf + strlen("BA_WSIZE_TX") + 1; |
| len = priv_cmd.total_len - strlen("BA_WSIZE_TX") - 1; |
| if (MLAN_STATUS_SUCCESS != |
| woal_set_tx_ba_winsize(priv, pdata, len)) { |
| ret = -EFAULT; |
| goto done; |
| } |
| len = sprintf(buf, "OK\n") + 1; |
| } else if (strncmp |
| (buf, "FAKE_SCAN_COMPLETE", |
| strlen("FAKE_SCAN_COMPLETE")) == 0) { |
| pdata = buf + strlen("FAKE_SCAN_COMPLETE") + 1; |
| #ifdef STA_CFG80211 |
| if (*pdata == '1') { |
| priv->fake_scan_complete = MTRUE; |
| PRINTM(MIOCTL, "fake scan complete enabled\n"); |
| } else if (*pdata == '0') { |
| priv->fake_scan_complete = MFALSE; |
| PRINTM(MIOCTL, "fake scan complete disabled\n"); |
| } |
| #endif |
| len = sprintf(buf, "OK\n") + 1; |
| } |
| #if defined(STA_CFG80211) || defined(UAP_CFG80211) |
| else if (strncmp |
| (buf, "P2P_PERIODIC_SLEEP", |
| strlen("P2P_PERIODIC_SLEEP")) == 0) { |
| if (MLAN_STATUS_SUCCESS != |
| woal_p2p_ps_cfg(priv, buf, priv_cmd.total_len)) { |
| ret = -EFAULT; |
| goto done; |
| } |
| len = sprintf(buf, "OK\n") + 1; |
| } |
| #endif |
| else if (strncmp(buf, "WLS_BATCHING", strlen("WLS_BATCHING")) == 0) { |
| /* TODO */ |
| len = sprintf(buf, "OK\n") + 1; |
| } else { |
| PRINTM(MIOCTL, "Unknown 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 (priv_cmd.used_len <= priv_cmd.total_len) { |
| memset(cmd_buf + priv_cmd.used_len, 0, |
| (size_t) (priv_cmd.total_len - |
| priv_cmd.used_len)); |
| if (copy_to_user(cmd_buf, buf, priv_cmd.used_len)) { |
| PRINTM(MERROR, |
| "%s: failed to copy data to user buffer\n", |
| __FUNCTION__); |
| ret = -EFAULT; |
| goto done; |
| } |
| if (copy_to_user |
| (req->ifr_data, &priv_cmd, |
| sizeof(android_wifi_priv_cmd))) { |
| PRINTM(MERROR, |
| "%s: failed to copy command header to user buffer\n", |
| __FUNCTION__); |
| ret = -EFAULT; |
| } |
| } else { |
| PRINTM(MERROR, |
| "%s: the buffer supplied by appl is too small (supplied: %d, used: %d)\n", |
| __FUNCTION__, priv_cmd.total_len, |
| priv_cmd.used_len); |
| ret = -EFAULT; |
| } |
| } else { |
| ret = len; |
| } |
| |
| done: |
| kfree(buf); |
| LEAVE(); |
| return ret; |
| } |
| |
| /******************************************************** |
| Global Functions |
| ********************************************************/ |
| /** |
| * @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 |
| */ |
| 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(); |
| |
| memcpy(ptmp_buf, pbss_desc->time_stamp, sizeof(pbss_desc->time_stamp)); |
| ptmp_buf += sizeof(pbss_desc->time_stamp); |
| |
| memcpy(ptmp_buf, &pbss_desc->beacon_period, |
| sizeof(pbss_desc->beacon_period)); |
| ptmp_buf += sizeof(pbss_desc->beacon_period); |
| |
| memcpy(ptmp_buf, &pbss_desc->cap_info, sizeof(pbss_desc->cap_info)); |
| 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; |
| memcpy(ptmp_buf, tmp_ssid_hdr, sizeof(tmp_ssid_hdr)); |
| ptmp_buf += sizeof(tmp_ssid_hdr); |
| |
| memcpy(ptmp_buf, pbss_desc->ssid.ssid, pbss_desc->ssid.ssid_len); |
| 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; |
| memcpy(ptmp_buf, &pbss_desc->wmm_ie, ie_len); |
| 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; |
| memcpy(ptmp_buf, pbss_desc->pwpa_ie, ie_len); |
| } |
| |
| 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; |
| memcpy(ptmp_buf, pbss_desc->prsn_ie, ie_len); |
| } |
| |
| 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 |
| */ |
| 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 |
| */ |
| memcpy(pcurrent, &tmp_rsp_entry, fixed_size); |
| pcurrent += fixed_size; |
| |
| if (pbss_desc->pbeacon_buf) { |
| /* |
| * Copy variable length elements to user space |
| */ |
| memcpy(pcurrent, pbss_desc->pbeacon_buf, |
| pbss_desc->beacon_buf_size); |
| |
| pcurrent += pbss_desc->beacon_buf_size; |
| } else { |
| wlan_scan_create_brief_table_entry(&pcurrent, pbss_desc); |
| } |
| |
| *ppbuffer = pcurrent; |
| |
| LEAVE(); |
| |
| return MLAN_STATUS_SUCCESS; |
| } |
| |
| /** |
| * @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) { |
| case WOAL_ANDROID_DEF_CMD: |
| /** android default ioctl ID is SIOCDEVPRIVATE + 1 */ |
| ret = woal_android_priv_cmd(dev, req); |
| break; |
| 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_TDLS_CONFIG: |
| ret = woal_tdls_config_ioctl(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; |
| } |