/** @file moal_priv.c * * @brief This file contains standard ioctl functions * * * Copyright 2008-2021 NXP * * NXP CONFIDENTIAL * The source code contained or described herein and all documents related to * the source code (Materials) are owned by NXP, its * suppliers and/or its licensors. Title to the Materials remains with NXP, * its suppliers and/or its licensors. The Materials contain * trade secrets and proprietary and confidential information of NXP, its * suppliers and/or its licensors. The Materials are protected by worldwide * copyright and trade secret laws and treaty provisions. No part of the * Materials may be used, copied, reproduced, modified, published, uploaded, * posted, transmitted, distributed, or disclosed in any way without NXP's prior * express written permission. * * No license under any patent, copyright, trade secret or other intellectual * property right is granted to or conferred upon you by disclosure or delivery * of the Materials, either expressly, by implication, inducement, estoppel or * otherwise. Any license under such intellectual property rights must be * express and approved by NXP in writing. * * Alternatively, this software may be distributed under the terms of GPL v2. * SPDX-License-Identifier: GPL-2.0 * */ /************************************************************************ Change log: 10/30/2008: initial version ************************************************************************/ #include "moal_main.h" #ifdef SDIO #include "moal_sdio.h" #endif /* SDIO */ #include "moal_eth_ioctl.h" #ifdef USB #include "moal_usb.h" #endif /******************************************************** Local Variables ********************************************************/ /** Bands supported in Infra mode */ static t_u8 SupportedInfraBand[] = { BAND_B, BAND_B | BAND_G, BAND_G, BAND_GN, BAND_B | BAND_G | BAND_GN, BAND_G | BAND_GN, BAND_A, BAND_B | BAND_A, BAND_B | BAND_G | BAND_A, BAND_G | BAND_A, BAND_A | BAND_B | BAND_G | BAND_AN | BAND_GN, BAND_A | BAND_G | BAND_AN | BAND_GN, BAND_A | BAND_AN, BAND_GN | BAND_GAC, BAND_B | BAND_G | BAND_GN | BAND_GAC, BAND_G | BAND_GN | BAND_GAC, BAND_A | BAND_B | BAND_G | BAND_AN | BAND_GN | BAND_AAC, BAND_A | BAND_G | BAND_AN | BAND_GN | BAND_AAC, BAND_A | BAND_AN | BAND_AAC, }; /** Bands supported in Ad-Hoc mode */ static t_u8 SupportedAdhocBand[] = { BAND_B, BAND_B | BAND_G, BAND_G, BAND_A, }; /******************************************************** Local Functions ********************************************************/ /** * @brief Associated to a specific indexed entry in the ScanTable * * @param priv A pointer to moal_private structure * @param req A pointer to ifreq structure * * @return 0 --success, otherwise fail */ static int woal_associate_ssid_bssid(moal_private *priv, struct iwreq *wrq) { mlan_ssid_bssid ssid_bssid; #ifdef REASSOCIATION mlan_bss_info bss_info; #endif char buf[64]; t_u8 buflen; t_u8 mac_idx; t_u8 i; ENTER(); memset(&ssid_bssid, 0, sizeof(ssid_bssid)); mac_idx = 0; buflen = MIN(wrq->u.data.length, (sizeof(buf) - 1)); memset(buf, 0, sizeof(buf)); if (buflen < (3 * ETH_ALEN) + 2) { PRINTM(MERROR, "Associate: Insufficient length in IOCTL input\n"); /* buffer should be at least 3 characters per BSSID octet "00:" ** plus a space separater and at least 1 char in the SSID */ LEAVE(); return -EINVAL; } if (copy_from_user(buf, wrq->u.data.pointer, buflen) != 0) { /* copy_from_user failed */ PRINTM(MERROR, "Associate: copy from user failed\n"); LEAVE(); return -EINVAL; } for (i = 0; (i < buflen) && (buf[i] == ' '); i++) { /* Skip white space */ } /* Copy/Convert the BSSID */ for (; (i < buflen) && (mac_idx < ETH_ALEN) && (buf[i] != ' '); i++) { if (buf[i] == ':') { mac_idx++; } else { if (mac_idx < ETH_ALEN) ssid_bssid.bssid[mac_idx] = (t_u8)woal_atox(buf + i); while ((i < buflen) && (isxdigit(buf[i + 1]))) { /* Skip entire hex value */ i++; } } } /* Skip one space between the BSSID and start of the SSID */ i++; /* Copy the SSID */ ssid_bssid.ssid.ssid_len = buflen - i - 1; moal_memcpy_ext(priv->phandle, ssid_bssid.ssid.ssid, buf + i, sizeof(ssid_bssid.ssid.ssid), sizeof(ssid_bssid.ssid.ssid)); PRINTM(MCMND, "iwpriv assoc: AP=[" MACSTR "], ssid(%d)=[%s]\n", MAC2STR(ssid_bssid.bssid), (int)ssid_bssid.ssid.ssid_len, ssid_bssid.ssid.ssid); if (MLAN_STATUS_SUCCESS != woal_bss_start(priv, MOAL_IOCTL_WAIT, &ssid_bssid)) { LEAVE(); return -EFAULT; } #ifdef REASSOCIATION memset(&bss_info, 0x00, sizeof(bss_info)); if (MLAN_STATUS_SUCCESS == woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info)) { moal_memcpy_ext(priv->phandle, &priv->prev_ssid_bssid.ssid, &bss_info.ssid, sizeof(mlan_802_11_ssid), sizeof(mlan_802_11_ssid)); moal_memcpy_ext(priv->phandle, &priv->prev_ssid_bssid.bssid, &bss_info.bssid, MLAN_MAC_ADDR_LENGTH, sizeof(mlan_802_11_mac_addr)); } #endif /* REASSOCIATION */ LEAVE(); return 0; } /** * @brief Copy Rates * * @param dest A pointer to destination buffer * @param pos The position for copy * @param src A pointer to source buffer * @param len Length of the source buffer * * @return Number of rates copied */ static inline int woal_copy_rates(t_u8 *dest, int pos, t_u8 *src, int len) { int i; for (i = 0; i < len && src[i]; i++, pos++) { if (pos >= MLAN_SUPPORTED_RATES) break; dest[pos] = src[i]; } return pos; } /** * @brief Performs warm reset * * @param priv A pointer to moal_private structure * * @return 0/MLAN_STATUS_SUCCESS --success, otherwise fail */ static int woal_warm_reset(moal_private *priv) { int ret = 0; moal_handle *handle = priv->phandle; moal_handle *ref_handle; moal_private *ref_priv; ENTER(); ret = woal_pre_warmreset(priv); if (ret) goto done; ref_handle = (moal_handle *)handle->pref_mac; if (ref_handle) { ref_priv = woal_get_priv(ref_handle, MLAN_BSS_ROLE_ANY); if (ref_priv) { ret = woal_pre_warmreset(ref_priv); if (ret) goto done; ret = woal_warmreset(ref_priv); if (ret) goto done; } } ret = woal_warmreset(priv); done: LEAVE(); return ret; } /** * @brief Get signal * * @param priv A pointer to moal_private structure * @param wrq A pointer to iwreq structure * * @return 0 --success, otherwise fail */ static int woal_get_signal(moal_private *priv, struct iwreq *wrq) { /** Input data size */ #define IN_DATA_SIZE 2 /** Output data size */ #define OUT_DATA_SIZE 12 int ret = 0; int in_data[IN_DATA_SIZE]; int out_data[OUT_DATA_SIZE]; mlan_ds_get_signal signal; int data_length = 0; int buflen = 0; ENTER(); memset(in_data, 0, sizeof(in_data)); memset(out_data, 0, sizeof(out_data)); buflen = MIN(wrq->u.data.length, IN_DATA_SIZE); if (priv->media_connected == MFALSE) { PRINTM(MERROR, "Can not get RSSI in disconnected state\n"); ret = -ENOTSUPP; goto done; } if (wrq->u.data.length) { if (sizeof(int) * wrq->u.data.length > sizeof(in_data)) { PRINTM(MERROR, "Too many arguments\n"); ret = -EINVAL; goto done; } if (copy_from_user(in_data, wrq->u.data.pointer, sizeof(int) * buflen)) { PRINTM(MERROR, "Copy from user failed\n"); ret = -EFAULT; goto done; } } switch (wrq->u.data.length) { case 0: /* No checking, get everything */ break; case 2: /* Check subtype range */ if (in_data[1] < 1 || in_data[1] > 4) { ret = -EINVAL; goto done; } /* Fall through */ case 1: /* Check type range */ if (in_data[0] < 1 || in_data[0] > 3) { ret = -EINVAL; goto done; } break; default: ret = -EINVAL; goto done; } memset(&signal, 0, sizeof(mlan_ds_get_signal)); if (MLAN_STATUS_SUCCESS != woal_get_signal_info(priv, MOAL_IOCTL_WAIT, &signal)) { ret = -EFAULT; goto done; } PRINTM(MINFO, "RSSI Beacon Last : %d\n", (int)signal.bcn_rssi_last); PRINTM(MINFO, "RSSI Beacon Average: %d\n", (int)signal.bcn_rssi_avg); PRINTM(MINFO, "RSSI Data Last : %d\n", (int)signal.data_rssi_last); PRINTM(MINFO, "RSSI Data Average : %d\n", (int)signal.data_rssi_avg); PRINTM(MINFO, "SNR Beacon Last : %d\n", (int)signal.bcn_snr_last); PRINTM(MINFO, "SNR Beacon Average : %d\n", (int)signal.bcn_snr_avg); PRINTM(MINFO, "SNR Data Last : %d\n", (int)signal.data_snr_last); PRINTM(MINFO, "SNR Data Average : %d\n", (int)signal.data_snr_avg); PRINTM(MINFO, "NF Beacon Last : %d\n", (int)signal.bcn_nf_last); PRINTM(MINFO, "NF Beacon Average : %d\n", (int)signal.bcn_nf_avg); PRINTM(MINFO, "NF Data Last : %d\n", (int)signal.data_nf_last); PRINTM(MINFO, "NF Data Average : %d\n", (int)signal.data_nf_avg); /* Check type */ switch (in_data[0]) { case 0: /* Send everything */ out_data[data_length++] = signal.bcn_rssi_last; out_data[data_length++] = signal.bcn_rssi_avg; out_data[data_length++] = signal.data_rssi_last; out_data[data_length++] = signal.data_rssi_avg; out_data[data_length++] = signal.bcn_snr_last; out_data[data_length++] = signal.bcn_snr_avg; out_data[data_length++] = signal.data_snr_last; out_data[data_length++] = signal.data_snr_avg; out_data[data_length++] = signal.bcn_nf_last; out_data[data_length++] = signal.bcn_nf_avg; out_data[data_length++] = signal.data_nf_last; out_data[data_length++] = signal.data_nf_avg; break; case 1: /* RSSI */ /* Check subtype */ switch (in_data[1]) { case 0: /* Everything */ out_data[data_length++] = signal.bcn_rssi_last; out_data[data_length++] = signal.bcn_rssi_avg; out_data[data_length++] = signal.data_rssi_last; out_data[data_length++] = signal.data_rssi_avg; break; case 1: /* bcn last */ out_data[data_length++] = signal.bcn_rssi_last; break; case 2: /* bcn avg */ out_data[data_length++] = signal.bcn_rssi_avg; break; case 3: /* data last */ out_data[data_length++] = signal.data_rssi_last; break; case 4: /* data avg */ out_data[data_length++] = signal.data_rssi_avg; break; default: break; } break; case 2: /* SNR */ /* Check subtype */ switch (in_data[1]) { case 0: /* Everything */ out_data[data_length++] = signal.bcn_snr_last; out_data[data_length++] = signal.bcn_snr_avg; out_data[data_length++] = signal.data_snr_last; out_data[data_length++] = signal.data_snr_avg; break; case 1: /* bcn last */ out_data[data_length++] = signal.bcn_snr_last; break; case 2: /* bcn avg */ out_data[data_length++] = signal.bcn_snr_avg; break; case 3: /* data last */ out_data[data_length++] = signal.data_snr_last; break; case 4: /* data avg */ out_data[data_length++] = signal.data_snr_avg; break; default: break; } break; case 3: /* NF */ /* Check subtype */ switch (in_data[1]) { case 0: /* Everything */ out_data[data_length++] = signal.bcn_nf_last; out_data[data_length++] = signal.bcn_nf_avg; out_data[data_length++] = signal.data_nf_last; out_data[data_length++] = signal.data_nf_avg; break; case 1: /* bcn last */ out_data[data_length++] = signal.bcn_nf_last; break; case 2: /* bcn avg */ out_data[data_length++] = signal.bcn_nf_avg; break; case 3: /* data last */ out_data[data_length++] = signal.data_nf_last; break; case 4: /* data avg */ out_data[data_length++] = signal.data_nf_avg; break; default: break; } break; default: break; } wrq->u.data.length = data_length; if (copy_to_user(wrq->u.data.pointer, out_data, wrq->u.data.length * sizeof(out_data[0]))) { PRINTM(MERROR, "Copy to user failed\n"); ret = -EFAULT; goto done; } done: LEAVE(); return ret; } /** * @brief Get/Set DeepSleep mode * * @param priv Pointer to the moal_private driver data struct * @param wreq A pointer to iwreq structure * * @return 0 --success, otherwise fail */ static int woal_deep_sleep_ioctl(moal_private *priv, struct iwreq *wrq) { int ret = 0; int user_data_len; t_u32 deep_sleep = DEEP_SLEEP_OFF; t_u32 data[2] = {0}; int copy_len; t_u16 idletime = DEEP_SLEEP_IDLE_TIME; ENTER(); user_data_len = wrq->u.data.length; copy_len = MIN(sizeof(data), sizeof(int) * user_data_len); if (user_data_len == 1 || user_data_len == 2) { if (copy_from_user(&data, wrq->u.data.pointer, copy_len)) { PRINTM(MERROR, "Copy from user failed\n"); LEAVE(); return -EFAULT; } deep_sleep = data[0]; if (deep_sleep == DEEP_SLEEP_OFF) { PRINTM(MINFO, "Exit Deep Sleep Mode\n"); ret = woal_set_deep_sleep(priv, MOAL_IOCTL_WAIT, MFALSE, 0); if (ret != MLAN_STATUS_SUCCESS) { LEAVE(); return -EINVAL; } } else if (deep_sleep == DEEP_SLEEP_ON) { PRINTM(MINFO, "Enter Deep Sleep Mode\n"); if (user_data_len == 2) idletime = data[1]; else idletime = 0; ret = woal_set_deep_sleep(priv, MOAL_IOCTL_WAIT, MTRUE, idletime); if (ret != MLAN_STATUS_SUCCESS) { LEAVE(); return -EINVAL; } } else { PRINTM(MERROR, "Unknown option = %u\n", deep_sleep); LEAVE(); return -EINVAL; } } else if (user_data_len > 2) { PRINTM(MERROR, "Invalid number of arguments %d\n", user_data_len); LEAVE(); return -EINVAL; } else { /* Display Deep Sleep settings */ PRINTM(MINFO, "Get Deep Sleep Mode\n"); if (MLAN_STATUS_SUCCESS != woal_get_deep_sleep(priv, data)) { LEAVE(); return -EFAULT; } if (data[0] == 0) wrq->u.data.length = 1; else wrq->u.data.length = 2; } /* Copy the Deep Sleep setting to user */ if (copy_to_user(wrq->u.data.pointer, data, wrq->u.data.length * sizeof(int))) { PRINTM(MERROR, "Copy to user failed\n"); LEAVE(); return -EINVAL; } LEAVE(); return 0; } /** * @brief Set/Get Usr 11n configuration request * * @param priv Pointer to the moal_private driver data struct * @param wrq A pointer to iwreq structure * * @return 0 --success, otherwise fail */ static int woal_11n_htcap_cfg(moal_private *priv, struct iwreq *wrq) { int data[2], copy_len; mlan_ioctl_req *req = NULL; mlan_ds_11n_cfg *cfg_11n = NULL; int ret = 0; int data_length = wrq->u.data.length; mlan_status status = MLAN_STATUS_SUCCESS; ENTER(); if (data_length > 2) { PRINTM(MERROR, "Invalid number of arguments\n"); ret = -EINVAL; goto done; } req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); if (req == NULL) { ret = -ENOMEM; goto done; } cfg_11n = (mlan_ds_11n_cfg *)req->pbuf; cfg_11n->sub_command = MLAN_OID_11N_HTCAP_CFG; req->req_id = MLAN_IOCTL_11N_CFG; copy_len = MIN(sizeof(data), sizeof(int) * data_length); if (data_length == 0) { /* Get 11n tx parameters from MLAN */ req->action = MLAN_ACT_GET; cfg_11n->param.htcap_cfg.misc_cfg = BAND_SELECT_BG; } else { if (copy_from_user(data, wrq->u.data.pointer, copy_len)) { PRINTM(MERROR, "Copy from user failed\n"); ret = -EFAULT; goto done; } cfg_11n->param.htcap_cfg.htcap = data[0]; PRINTM(MINFO, "SET: htcapinfo:0x%x\n", data[0]); cfg_11n->param.htcap_cfg.misc_cfg = BAND_SELECT_BOTH; if (data_length == 2) { if (data[1] != BAND_SELECT_BG && data[1] != BAND_SELECT_A && data[1] != BAND_SELECT_BOTH) { PRINTM(MERROR, "Invalid band selection\n"); ret = -EINVAL; goto done; } cfg_11n->param.htcap_cfg.misc_cfg = data[1]; PRINTM(MINFO, "SET: htcapinfo band:0x%x\n", data[1]); } /* Update 11n tx parameters in MLAN */ req->action = MLAN_ACT_SET; } status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto done; } data[0] = cfg_11n->param.htcap_cfg.htcap; if (req->action == MLAN_ACT_GET) { data_length = 1; cfg_11n->param.htcap_cfg.htcap = 0; cfg_11n->param.htcap_cfg.misc_cfg = BAND_SELECT_A; status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto done; } if ((int)cfg_11n->param.htcap_cfg.htcap != data[0]) { data_length = 2; data[1] = cfg_11n->param.htcap_cfg.htcap; PRINTM(MINFO, "GET: htcapinfo for 2.4GHz:0x%x\n", data[0]); PRINTM(MINFO, "GET: htcapinfo for 5GHz:0x%x\n", data[1]); } else PRINTM(MINFO, "GET: htcapinfo:0x%x\n", data[0]); } if (copy_to_user(wrq->u.data.pointer, data, sizeof(data))) { PRINTM(MERROR, "Copy to user failed\n"); ret = -EFAULT; goto done; } wrq->u.data.length = data_length; done: if (status != MLAN_STATUS_PENDING) kfree(req); LEAVE(); return ret; } /** * @brief Enable/Disable amsdu_aggr_ctrl * * @param priv Pointer to the moal_private driver data struct * @param wrq A pointer to iwreq structure * * @return 0 --success, otherwise fail */ static int woal_11n_amsdu_aggr_ctrl(moal_private *priv, struct iwreq *wrq) { int data[2], copy_len; mlan_ioctl_req *req = NULL; mlan_ds_11n_cfg *cfg_11n = NULL; int ret = 0; int data_length = wrq->u.data.length; mlan_status status = MLAN_STATUS_SUCCESS; ENTER(); if ((data_length != 0) && (data_length != 1)) { PRINTM(MERROR, "Invalid number of arguments\n"); ret = -EINVAL; goto done; } copy_len = MIN(sizeof(data), sizeof(int) * data_length); req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); if (req == NULL) { ret = -ENOMEM; goto done; } cfg_11n = (mlan_ds_11n_cfg *)req->pbuf; cfg_11n->sub_command = MLAN_OID_11N_CFG_AMSDU_AGGR_CTRL; req->req_id = MLAN_IOCTL_11N_CFG; if (data_length == 0) { /* Get 11n tx parameters from MLAN */ req->action = MLAN_ACT_GET; } else if (data_length == 1) { if (copy_from_user(data, wrq->u.data.pointer, copy_len)) { PRINTM(MERROR, "Copy from user failed\n"); ret = -EFAULT; goto done; } cfg_11n->param.amsdu_aggr_ctrl.enable = data[0]; /* Update 11n tx parameters in MLAN */ req->action = MLAN_ACT_SET; } status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto done; } data[0] = cfg_11n->param.amsdu_aggr_ctrl.enable; data[1] = cfg_11n->param.amsdu_aggr_ctrl.curr_buf_size; if (copy_to_user(wrq->u.data.pointer, data, sizeof(data))) { PRINTM(MERROR, "Copy to user failed\n"); ret = -EFAULT; goto done; } wrq->u.data.length = 2; done: if (status != MLAN_STATUS_PENDING) kfree(req); LEAVE(); return ret; } /** * @brief Set/Get 11n configuration request * * @param priv Pointer to the moal_private driver data struct * @param wrq A pointer to iwreq structure * * @return 0 --success, otherwise fail */ static int woal_11n_tx_cfg(moal_private *priv, struct iwreq *wrq) { int data[2], copy_len; mlan_ioctl_req *req = NULL; mlan_ds_11n_cfg *cfg_11n = NULL; int ret = 0; int data_length = wrq->u.data.length; mlan_status status = MLAN_STATUS_SUCCESS; ENTER(); if (data_length > 2) { PRINTM(MERROR, "Invalid number of arguments\n"); ret = -EINVAL; goto done; } copy_len = MIN(sizeof(data), sizeof(int) * data_length); req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); if (req == NULL) { ret = -ENOMEM; goto done; } cfg_11n = (mlan_ds_11n_cfg *)req->pbuf; cfg_11n->sub_command = MLAN_OID_11N_CFG_TX; req->req_id = MLAN_IOCTL_11N_CFG; if (data_length == 0) { /* Get 11n tx parameters from MLAN */ req->action = MLAN_ACT_GET; cfg_11n->param.tx_cfg.misc_cfg = BAND_SELECT_BG; } else { if (copy_from_user(data, wrq->u.data.pointer, copy_len)) { PRINTM(MERROR, "Copy from user failed\n"); ret = -EFAULT; goto done; } cfg_11n->param.tx_cfg.httxcap = data[0]; PRINTM(MINFO, "SET: httxcap:0x%x\n", data[0]); cfg_11n->param.tx_cfg.misc_cfg = BAND_SELECT_BOTH; if (data_length == 2) { if (data[1] != BAND_SELECT_BG && data[1] != BAND_SELECT_A && data[1] != BAND_SELECT_BOTH) { PRINTM(MERROR, "Invalid band selection\n"); ret = -EINVAL; goto done; } cfg_11n->param.tx_cfg.misc_cfg = data[1]; PRINTM(MINFO, "SET: httxcap band:0x%x\n", data[1]); } /* Update 11n tx parameters in MLAN */ req->action = MLAN_ACT_SET; } status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto done; } data[0] = cfg_11n->param.tx_cfg.httxcap; if (req->action == MLAN_ACT_GET) { data_length = 1; cfg_11n->param.tx_cfg.httxcap = 0; cfg_11n->param.tx_cfg.misc_cfg = BAND_SELECT_A; status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto done; } if (cfg_11n->param.tx_cfg.httxcap != data[0]) { data_length = 2; data[1] = cfg_11n->param.tx_cfg.httxcap; PRINTM(MINFO, "GET: httxcap for 2.4GHz:0x%x\n", data[0]); PRINTM(MINFO, "GET: httxcap for 5GHz:0x%x\n", data[1]); } else PRINTM(MINFO, "GET: httxcap:0x%x\n", data[0]); } if (copy_to_user(wrq->u.data.pointer, data, sizeof(data))) { PRINTM(MERROR, "Copy to user failed\n"); ret = -EFAULT; goto done; } wrq->u.data.length = data_length; done: if (status != MLAN_STATUS_PENDING) kfree(req); LEAVE(); return ret; } /** * @brief Enable/Disable TX Aggregation * * @param priv Pointer to the moal_private driver data struct * @param wrq A pointer to iwreq structure * * @return 0 --success, otherwise fail */ static int woal_11n_prio_tbl(moal_private *priv, struct iwreq *wrq) { int data[MAX_NUM_TID * 2], i, j, copy_len; mlan_ioctl_req *req = NULL; mlan_ds_11n_cfg *cfg_11n = NULL; int ret = 0; int data_length = wrq->u.data.length; mlan_status status = MLAN_STATUS_SUCCESS; ENTER(); if (wrq->u.data.pointer == NULL) { LEAVE(); return -EINVAL; } copy_len = MIN(sizeof(data), sizeof(int) * data_length); req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); if (req == NULL) { LEAVE(); return -ENOMEM; } cfg_11n = (mlan_ds_11n_cfg *)req->pbuf; cfg_11n->sub_command = MLAN_OID_11N_CFG_AGGR_PRIO_TBL; req->req_id = MLAN_IOCTL_11N_CFG; if (data_length == 0) { /* Get aggr priority table from MLAN */ req->action = MLAN_ACT_GET; status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto error; } wrq->u.data.length = MAX_NUM_TID * 2; for (i = 0, j = 0; i < (wrq->u.data.length); i = i + 2, ++j) { data[i] = cfg_11n->param.aggr_prio_tbl.ampdu[j]; data[i + 1] = cfg_11n->param.aggr_prio_tbl.amsdu[j]; } if (copy_to_user(wrq->u.data.pointer, data, sizeof(int) * wrq->u.data.length)) { PRINTM(MERROR, "Copy to user failed\n"); ret = -EFAULT; goto error; } } else if (data_length == 16) { if (copy_from_user(data, wrq->u.data.pointer, copy_len)) { PRINTM(MERROR, "Copy from user failed\n"); ret = -EFAULT; goto error; } for (i = 0, j = 0; i < (data_length); i = i + 2, ++j) { if ((data[i] > 7 && data[i] != 0xff) || (data[i + 1] > 7 && data[i + 1] != 0xff)) { PRINTM(MERROR, "Invalid priority, valid value 0-7 or 0xff.\n"); ret = -EFAULT; goto error; } cfg_11n->param.aggr_prio_tbl.ampdu[j] = data[i]; cfg_11n->param.aggr_prio_tbl.amsdu[j] = data[i + 1]; } /* Update aggr priority table in MLAN */ req->action = MLAN_ACT_SET; status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto error; } } else { PRINTM(MERROR, "Invalid number of arguments\n"); ret = -EINVAL; goto error; } error: if (status != MLAN_STATUS_PENDING) kfree(req); LEAVE(); return ret; } /** * @brief Set/Get add BA Reject parameters * * @param priv Pointer to the moal_private driver data struct * @param wrq A pointer to iwreq structure * * @return 0 --success, otherwise fail */ static int woal_addba_reject(moal_private *priv, struct iwreq *wrq) { int data[MAX_NUM_TID], ret = 0, i, copy_len; mlan_ioctl_req *req = NULL; mlan_ds_11n_cfg *cfg_11n = NULL; int data_length = wrq->u.data.length; mlan_status status = MLAN_STATUS_SUCCESS; ENTER(); copy_len = MIN(sizeof(data), sizeof(int) * data_length); req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); if (req == NULL) { LEAVE(); return -ENOMEM; } cfg_11n = (mlan_ds_11n_cfg *)req->pbuf; cfg_11n->sub_command = MLAN_OID_11N_CFG_ADDBA_REJECT; req->req_id = MLAN_IOCTL_11N_CFG; if (data_length == 0) { PRINTM(MERROR, "Addba reject moal\n"); /* Get aggr priority table from MLAN */ req->action = MLAN_ACT_GET; status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto error; } wrq->u.data.length = MAX_NUM_TID; for (i = 0; i < (wrq->u.data.length); ++i) data[i] = cfg_11n->param.addba_reject[i]; if (copy_to_user(wrq->u.data.pointer, data, sizeof(int) * wrq->u.data.length)) { PRINTM(MERROR, "Copy to user failed\n"); ret = -EFAULT; goto error; } } else if (data_length == 8) { if (copy_from_user(data, wrq->u.data.pointer, copy_len)) { PRINTM(MERROR, "Copy from user failed\n"); ret = -EFAULT; goto error; } for (i = 0; i < (data_length); ++i) { if (data[i] != 0 && data[i] != 1) { PRINTM(MERROR, "addba reject only takes argument as 0 or 1\n"); ret = -EFAULT; goto error; } cfg_11n->param.addba_reject[i] = data[i]; } /* Update aggr priority table in MLAN */ req->action = MLAN_ACT_SET; status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto error; } } else { PRINTM(MERROR, "Invalid number of arguments\n"); ret = -EINVAL; goto error; } error: if (status != MLAN_STATUS_PENDING) kfree(req); LEAVE(); return ret; } /** * @brief Set/Get add BA parameters * * @param priv Pointer to the moal_private driver data struct * @param wrq A pointer to iwreq structure * * @return 0 --success, otherwise fail */ static int woal_addba_para_updt(moal_private *priv, struct iwreq *wrq) { int data[5], ret = 0, copy_len; mlan_ioctl_req *req = NULL; mlan_ds_11n_cfg *cfg_11n = NULL; int data_length = wrq->u.data.length; mlan_status status = MLAN_STATUS_SUCCESS; ENTER(); copy_len = MIN(sizeof(data), sizeof(int) * data_length); req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); if (req == NULL) { LEAVE(); return -ENOMEM; } cfg_11n = (mlan_ds_11n_cfg *)req->pbuf; cfg_11n->sub_command = MLAN_OID_11N_CFG_ADDBA_PARAM; req->req_id = MLAN_IOCTL_11N_CFG; if (data_length == 0) { /* Get Add BA parameters from MLAN */ req->action = MLAN_ACT_GET; status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto error; } data[0] = cfg_11n->param.addba_param.timeout; data[1] = cfg_11n->param.addba_param.txwinsize; data[2] = cfg_11n->param.addba_param.rxwinsize; data[3] = cfg_11n->param.addba_param.txamsdu; data[4] = cfg_11n->param.addba_param.rxamsdu; PRINTM(MINFO, "GET: timeout:%d txwinsize:%d rxwinsize:%d txamsdu=%d, rxamsdu=%d\n", data[0], data[1], data[2], data[3], data[4]); wrq->u.data.length = 5; if (copy_to_user(wrq->u.data.pointer, data, wrq->u.data.length * sizeof(int))) { PRINTM(MERROR, "Copy to user failed\n"); ret = -EFAULT; goto error; } } else if (data_length == 5) { if (copy_from_user(data, wrq->u.data.pointer, copy_len)) { PRINTM(MERROR, "Copy from user failed\n"); ret = -EFAULT; goto error; } if (data[0] < 0 || data[0] > MLAN_DEFAULT_BLOCK_ACK_TIMEOUT) { PRINTM(MERROR, "Incorrect addba timeout value.\n"); ret = -EFAULT; goto error; } if (data[1] <= 0 || data[1] > MLAN_AMPDU_MAX_TXWINSIZE || data[2] <= 0 || data[2] > MLAN_AMPDU_MAX_RXWINSIZE) { PRINTM(MERROR, "Incorrect Tx/Rx window size.\n"); ret = -EFAULT; goto error; } cfg_11n->param.addba_param.timeout = data[0]; cfg_11n->param.addba_param.txwinsize = data[1]; cfg_11n->param.addba_param.rxwinsize = data[2]; if (data[3] < 0 || data[3] > 1 || data[4] < 0 || data[4] > 1) { PRINTM(MERROR, "Incorrect Tx/Rx amsdu.\n"); ret = -EFAULT; goto error; } cfg_11n->param.addba_param.txamsdu = data[3]; cfg_11n->param.addba_param.rxamsdu = data[4]; PRINTM(MINFO, "SET: timeout:%d txwinsize:%d rxwinsize:%d txamsdu=%d rxamsdu=%d\n", data[0], data[1], data[2], data[3], data[4]); /* Update Add BA parameters in MLAN */ req->action = MLAN_ACT_SET; status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = MLAN_STATUS_FAILURE; goto error; } } else { PRINTM(MERROR, "Invalid number of arguments\n"); ret = -EINVAL; goto error; } error: if (status != MLAN_STATUS_PENDING) kfree(req); LEAVE(); return ret; } /** * @brief Set/Get Transmit buffer size * * @param priv Pointer to the moal_private driver data struct * @param wrq A pointer to iwreq structure * * @return 0 --success, otherwise fail */ static int woal_txbuf_cfg(moal_private *priv, struct iwreq *wrq) { int buf_size; mlan_ioctl_req *req = NULL; mlan_ds_11n_cfg *cfg_11n = NULL; int ret = 0; mlan_status status = MLAN_STATUS_SUCCESS; ENTER(); req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); if (req == NULL) { ret = -ENOMEM; goto done; } cfg_11n = (mlan_ds_11n_cfg *)req->pbuf; cfg_11n->sub_command = MLAN_OID_11N_CFG_MAX_TX_BUF_SIZE; req->req_id = MLAN_IOCTL_11N_CFG; if (wrq->u.data.length == 0) { /* Get Tx buffer size from MLAN */ req->action = MLAN_ACT_GET; } else { ret = -EINVAL; PRINTM(MERROR, "Don't support set Tx buffer size after driver loaded!\n"); goto done; } status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto done; } buf_size = cfg_11n->param.tx_buf_size; if (copy_to_user(wrq->u.data.pointer, &buf_size, sizeof(buf_size))) { PRINTM(MERROR, "Copy to user failed\n"); ret = -EFAULT; goto done; } wrq->u.data.length = 1; done: if (status != MLAN_STATUS_PENDING) kfree(req); LEAVE(); return ret; } /** * @brief Set/Get Host Sleep configuration * * @param priv A pointer to moal_private structure * @param wrq A pointer to iwreq structure * @param invoke_hostcmd MTRUE --invoke HostCmd, otherwise MFALSE * * @return 0 --success, otherwise fail */ static int woal_hs_cfg(moal_private *priv, struct iwreq *wrq, BOOLEAN invoke_hostcmd) { int data[3], copy_len; int ret = 0; mlan_ds_hs_cfg hscfg; t_u16 action; mlan_bss_info bss_info; int data_length = wrq->u.data.length; ENTER(); memset(data, 0, sizeof(data)); memset(&hscfg, 0, sizeof(mlan_ds_hs_cfg)); copy_len = MIN(sizeof(data), sizeof(int) * data_length); if (data_length == 0) { action = MLAN_ACT_GET; } else { action = MLAN_ACT_SET; if (data_length >= 1 && data_length <= 3) { if (copy_from_user(data, wrq->u.data.pointer, copy_len)) { PRINTM(MERROR, "Copy from user failed\n"); ret = -EFAULT; goto done; } } else { PRINTM(MERROR, "Invalid arguments\n"); ret = -EINVAL; goto done; } } /* HS config is blocked if HS is already activated */ if (data_length && (data[0] != (int)HOST_SLEEP_CFG_CANCEL || invoke_hostcmd == MFALSE)) { memset(&bss_info, 0, sizeof(bss_info)); woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info); if (bss_info.is_hs_configured) { PRINTM(MERROR, "HS already configured\n"); ret = -EFAULT; goto done; } } /* Do a GET first if some arguments are not provided */ if (data_length >= 1 && data_length < 3) { woal_set_get_hs_params(priv, MLAN_ACT_GET, MOAL_IOCTL_WAIT, &hscfg); } if (data_length) hscfg.conditions = data[0]; if (data_length >= 2) hscfg.gpio = data[1]; if (data_length == 3) hscfg.gap = data[2]; if ((invoke_hostcmd == MTRUE) && (action == MLAN_ACT_SET)) { /* Need to issue an extra IOCTL first to set up parameters */ hscfg.is_invoke_hostcmd = MFALSE; if (MLAN_STATUS_SUCCESS != woal_set_get_hs_params(priv, MLAN_ACT_SET, MOAL_IOCTL_WAIT, &hscfg)) { ret = -EFAULT; goto done; } } hscfg.is_invoke_hostcmd = invoke_hostcmd; if (MLAN_STATUS_SUCCESS != woal_set_get_hs_params(priv, action, MOAL_IOCTL_WAIT, &hscfg)) { ret = -EFAULT; goto done; } if (action == MLAN_ACT_GET) { data[0] = hscfg.conditions; data[1] = hscfg.gpio; data[2] = hscfg.gap; wrq->u.data.length = 3; if (copy_to_user(wrq->u.data.pointer, data, sizeof(int) * wrq->u.data.length)) { PRINTM(MERROR, "Copy to user failed\n"); ret = -EFAULT; goto done; } } done: LEAVE(); return ret; } /** * @brief Set Host Sleep parameters * * @param priv A pointer to moal_private structure * @param wrq A pointer to iwreq structure * * @return 0 --success, otherwise fail */ static int woal_hs_setpara(moal_private *priv, struct iwreq *wrq) { int ret = 0; int data_length = wrq->u.data.length; ENTER(); if (data_length >= 1 && data_length <= 3) { ret = woal_hs_cfg(priv, wrq, MFALSE); goto done; } else { PRINTM(MERROR, "Invalid arguments\n"); ret = -EINVAL; goto done; } done: LEAVE(); return ret; } /** * @brief Get/Set inactivity timeout extend * * @param priv A pointer to moal_private structure * @param wrq A pointer to iwreq structure * * @return 0 --success, otherwise fail */ static int woal_inactivity_timeout_ext(moal_private *priv, struct iwreq *wrq) { int data[4], copy_len; int ret = 0; mlan_ioctl_req *req = NULL; mlan_ds_pm_cfg *pmcfg = NULL; pmlan_ds_inactivity_to inac_to = NULL; int data_length = wrq->u.data.length; mlan_status status = MLAN_STATUS_SUCCESS; ENTER(); copy_len = MIN(sizeof(data), sizeof(int) * data_length); memset(data, 0, sizeof(data)); req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_pm_cfg)); if (req == NULL) { ret = -ENOMEM; goto done; } pmcfg = (mlan_ds_pm_cfg *)req->pbuf; inac_to = &pmcfg->param.inactivity_to; pmcfg->sub_command = MLAN_OID_PM_CFG_INACTIVITY_TO; req->req_id = MLAN_IOCTL_PM_CFG; if ((data_length != 0 && data_length != 3 && data_length != 4) || sizeof(int) * data_length > sizeof(data)) { PRINTM(MERROR, "Invalid number of parameters\n"); ret = -EINVAL; goto done; } req->action = MLAN_ACT_GET; if (data_length) { if (copy_from_user(data, wrq->u.data.pointer, copy_len)) { PRINTM(MERROR, "Copy from user failed\n"); ret = -EFAULT; goto done; } req->action = MLAN_ACT_SET; inac_to->timeout_unit = data[0]; inac_to->unicast_timeout = data[1]; inac_to->mcast_timeout = data[2]; inac_to->ps_entry_timeout = data[3]; } status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto done; } /* Copy back current values regardless of GET/SET */ data[0] = inac_to->timeout_unit; data[1] = inac_to->unicast_timeout; data[2] = inac_to->mcast_timeout; data[3] = inac_to->ps_entry_timeout; if (req->action == MLAN_ACT_GET) { wrq->u.data.length = 4; if (copy_to_user(wrq->u.data.pointer, data, wrq->u.data.length * sizeof(int))) { PRINTM(MERROR, "Copy to user failed\n"); ret = -EFAULT; goto done; } } done: if (status != MLAN_STATUS_PENDING) kfree(req); LEAVE(); return ret; } /** * @brief Set/Get system clock * * @param priv A pointer to moal_private structure * @param wrq A pointer to iwreq structure * * @return 0 --success, otherwise fail */ static int woal_ecl_sys_clock(moal_private *priv, struct iwreq *wrq) { int data[64], copy_len; int ret = 0; mlan_ioctl_req *req = NULL; mlan_ds_misc_cfg *cfg = NULL; int data_length = wrq->u.data.length; int i = 0; mlan_status status = MLAN_STATUS_SUCCESS; ENTER(); memset(data, 0, sizeof(data)); copy_len = MIN(sizeof(data), sizeof(int) * data_length); req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); if (req == NULL) { ret = -ENOMEM; goto done; } cfg = (mlan_ds_misc_cfg *)req->pbuf; cfg->sub_command = MLAN_OID_MISC_SYS_CLOCK; req->req_id = MLAN_IOCTL_MISC_CFG; if (!data_length) req->action = MLAN_ACT_GET; else if (data_length <= MLAN_MAX_CLK_NUM) { req->action = MLAN_ACT_SET; if (copy_from_user(data, wrq->u.data.pointer, copy_len)) { PRINTM(MERROR, "Copy from user failed\n"); ret = -EFAULT; goto done; } } else { PRINTM(MERROR, "Invalid arguments\n"); ret = -EINVAL; goto done; } if (req->action == MLAN_ACT_GET) { /* Get configurable clocks */ cfg->param.sys_clock.sys_clk_type = MLAN_CLK_CONFIGURABLE; status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto done; } /* Current system clock */ data[0] = (int)cfg->param.sys_clock.cur_sys_clk; wrq->u.data.length = 1; data_length = MIN(cfg->param.sys_clock.sys_clk_num, MLAN_MAX_CLK_NUM); /* Configurable clocks */ for (i = 0; i < data_length; i++) { data[i + wrq->u.data.length] = (int)cfg->param.sys_clock.sys_clk[i]; } wrq->u.data.length += data_length; /* Get supported clocks */ cfg->param.sys_clock.sys_clk_type = MLAN_CLK_SUPPORTED; status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto done; } data_length = MIN(cfg->param.sys_clock.sys_clk_num, MLAN_MAX_CLK_NUM); /* Supported clocks */ for (i = 0; i < data_length; i++) { data[i + wrq->u.data.length] = (int)cfg->param.sys_clock.sys_clk[i]; } wrq->u.data.length += data_length; if (copy_to_user(wrq->u.data.pointer, data, sizeof(int) * wrq->u.data.length)) { PRINTM(MERROR, "Copy to user failed\n"); ret = -EFAULT; goto done; } } else { /* Set configurable clocks */ cfg->param.sys_clock.sys_clk_type = MLAN_CLK_CONFIGURABLE; cfg->param.sys_clock.sys_clk_num = MIN(MLAN_MAX_CLK_NUM, data_length); for (i = 0; i < cfg->param.sys_clock.sys_clk_num; i++) cfg->param.sys_clock.sys_clk[i] = (t_u16)data[i]; status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto done; } } done: if (status != MLAN_STATUS_PENDING) kfree(req); LEAVE(); return ret; } /** * @brief Set/Get Band and Adhoc-band setting * * @param priv A pointer to moal_private structure * @param wrq A pointer to iwreq structure * * @return 0 --success, otherwise fail */ static int woal_band_cfg(moal_private *priv, struct iwreq *wrq) { int ret = 0; unsigned int i; int data[3]; int user_data_len = wrq->u.data.length, copy_len; t_u32 infra_band = 0; t_u32 adhoc_band = 0; t_u32 adhoc_channel = 0; mlan_ioctl_req *req = NULL; mlan_ds_radio_cfg *radio_cfg = NULL; mlan_status status = MLAN_STATUS_SUCCESS; ENTER(); if (sizeof(int) * user_data_len > sizeof(data)) { PRINTM(MERROR, "Too many arguments\n"); LEAVE(); return -EINVAL; } if (user_data_len > 0) { if (priv->media_connected == MTRUE) { LEAVE(); return -EOPNOTSUPP; } } copy_len = MIN(sizeof(data), sizeof(int) * user_data_len); req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_radio_cfg)); if (req == NULL) { ret = -ENOMEM; goto error; } radio_cfg = (mlan_ds_radio_cfg *)req->pbuf; radio_cfg->sub_command = MLAN_OID_BAND_CFG; req->req_id = MLAN_IOCTL_RADIO_CFG; if (wrq->u.data.length == 0) { /* Get config_bands, adhoc_start_band and adhoc_channel values * from MLAN */ req->action = MLAN_ACT_GET; status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto error; } /* Infra Band */ data[0] = radio_cfg->param.band_cfg.config_bands; /* Adhoc Band */ data[1] = radio_cfg->param.band_cfg.adhoc_start_band; /* Adhoc Channel */ data[2] = radio_cfg->param.band_cfg.adhoc_channel; wrq->u.data.length = 3; if (copy_to_user(wrq->u.data.pointer, data, sizeof(int) * wrq->u.data.length)) { ret = -EFAULT; goto error; } } else { if (copy_from_user(data, wrq->u.data.pointer, copy_len)) { PRINTM(MERROR, "Copy from user failed\n"); ret = -EFAULT; goto error; } /* To support only */ infra_band = data[0]; for (i = 0; i < sizeof(SupportedInfraBand); i++) if (infra_band == SupportedInfraBand[i]) break; if (i == sizeof(SupportedInfraBand)) { ret = -EINVAL; goto error; } /* Set Adhoc band */ if (user_data_len >= 2) { adhoc_band = data[1]; for (i = 0; i < sizeof(SupportedAdhocBand); i++) if (adhoc_band == SupportedAdhocBand[i]) break; if (i == sizeof(SupportedAdhocBand)) { ret = -EINVAL; goto error; } } /* Set Adhoc channel */ if (user_data_len >= 3) { adhoc_channel = data[2]; if (adhoc_channel == 0) { /* Check if specified adhoc channel is non-zero */ ret = -EINVAL; goto error; } } /* Set config_bands and adhoc_start_band values to MLAN */ req->action = MLAN_ACT_SET; radio_cfg->param.band_cfg.config_bands = infra_band; radio_cfg->param.band_cfg.adhoc_start_band = adhoc_band; radio_cfg->param.band_cfg.adhoc_channel = adhoc_channel; status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto error; } } error: if (status != MLAN_STATUS_PENDING) kfree(req); LEAVE(); return ret; } /** * @brief Read/Write adapter registers value * * @param priv A pointer to moal_private structure * @param wrq A pointer to iwreq structure * * @return 0 --success, otherwise fail */ static int woal_reg_read_write(moal_private *priv, struct iwreq *wrq) { int data[3], copy_len; int ret = 0; mlan_ioctl_req *req = NULL; mlan_ds_reg_mem *reg = NULL; int data_length = wrq->u.data.length; mlan_status status = MLAN_STATUS_SUCCESS; ENTER(); memset(data, 0, sizeof(data)); copy_len = MIN(sizeof(data), sizeof(int) * data_length); req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_reg_mem)); if (req == NULL) { ret = -ENOMEM; goto done; } reg = (mlan_ds_reg_mem *)req->pbuf; reg->sub_command = MLAN_OID_REG_RW; req->req_id = MLAN_IOCTL_REG_MEM; if (data_length == 2) { req->action = MLAN_ACT_GET; } else if (data_length == 3) { req->action = MLAN_ACT_SET; } else { ret = -EINVAL; goto done; } if (copy_from_user(data, wrq->u.data.pointer, copy_len)) { PRINTM(MERROR, "Copy from user failed\n"); ret = -EFAULT; goto done; } reg->param.reg_rw.type = (t_u32)data[0]; reg->param.reg_rw.offset = (t_u32)data[1]; if (data_length == 3) reg->param.reg_rw.value = (t_u32)data[2]; status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto done; } if (req->action == MLAN_ACT_GET) { if (copy_to_user(wrq->u.data.pointer, ®->param.reg_rw.value, sizeof(int))) { PRINTM(MERROR, "Copy to user failed\n"); ret = -EFAULT; goto done; } wrq->u.data.length = 1; } done: if (status != MLAN_STATUS_PENDING) kfree(req); LEAVE(); return ret; } /** * @brief Read the EEPROM contents of the card * * @param priv A pointer to moal_private structure * @param wrq A pointer to iwreq structure * * @return 0 --success, otherwise fail */ static int woal_read_eeprom(moal_private *priv, struct iwreq *wrq) { int data[2], copy_len; int ret = 0; mlan_ioctl_req *req = NULL; mlan_ds_reg_mem *reg = NULL; int data_length = wrq->u.data.length; mlan_status status = MLAN_STATUS_SUCCESS; ENTER(); memset(data, 0, sizeof(data)); copy_len = MIN(sizeof(data), sizeof(int) * data_length); req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_reg_mem)); if (req == NULL) { ret = -ENOMEM; goto done; } reg = (mlan_ds_reg_mem *)req->pbuf; reg->sub_command = MLAN_OID_EEPROM_RD; req->req_id = MLAN_IOCTL_REG_MEM; if (data_length == 2) { req->action = MLAN_ACT_GET; } else { ret = -EINVAL; goto done; } if (copy_from_user(data, wrq->u.data.pointer, copy_len)) { PRINTM(MERROR, "Copy from user failed\n"); ret = -EFAULT; goto done; } reg->param.rd_eeprom.offset = (t_u16)data[0]; reg->param.rd_eeprom.byte_count = (t_u16)data[1]; status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto done; } if (req->action == MLAN_ACT_GET) { wrq->u.data.length = reg->param.rd_eeprom.byte_count; if (copy_to_user(wrq->u.data.pointer, reg->param.rd_eeprom.value, MIN(wrq->u.data.length, MAX_EEPROM_DATA))) { PRINTM(MERROR, "Copy to user failed\n"); ret = -EFAULT; goto done; } } done: if (status != MLAN_STATUS_PENDING) kfree(req); LEAVE(); return ret; } /** * @brief Read/Write device memory value * * @param priv A pointer to moal_private structure * @param wrq A pointer to iwreq structure * * @return 0 --success, otherwise fail */ static int woal_mem_read_write(moal_private *priv, struct iwreq *wrq) { t_u32 data[2]; int ret = 0; mlan_ioctl_req *req = NULL; mlan_ds_reg_mem *reg_mem = NULL; int data_length = wrq->u.data.length, copy_len; mlan_status status = MLAN_STATUS_SUCCESS; ENTER(); memset(data, 0, sizeof(data)); copy_len = MIN(sizeof(data), sizeof(int) * data_length); req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_reg_mem)); if (req == NULL) { ret = -ENOMEM; goto done; } reg_mem = (mlan_ds_reg_mem *)req->pbuf; reg_mem->sub_command = MLAN_OID_MEM_RW; req->req_id = MLAN_IOCTL_REG_MEM; if (data_length == 1) { PRINTM(MINFO, "MEM_RW: GET\n"); req->action = MLAN_ACT_GET; } else if (data_length == 2) { PRINTM(MINFO, "MEM_RW: SET\n"); req->action = MLAN_ACT_SET; } else { ret = -EINVAL; goto done; } if (copy_from_user(data, wrq->u.data.pointer, copy_len)) { PRINTM(MERROR, "Copy from user failed\n"); ret = -EFAULT; goto done; } reg_mem->param.mem_rw.addr = (t_u32)data[0]; if (data_length == 2) reg_mem->param.mem_rw.value = (t_u32)data[1]; PRINTM(MINFO, "MEM_RW: Addr=0x%x, Value=0x%x\n", (int)reg_mem->param.mem_rw.addr, (int)reg_mem->param.mem_rw.value); status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto done; } if (req->action == MLAN_ACT_GET) { if (copy_to_user(wrq->u.data.pointer, ®_mem->param.mem_rw.value, sizeof(int))) { PRINTM(MERROR, "Copy to user failed\n"); ret = -EFAULT; goto done; } wrq->u.data.length = 1; } done: if (status != MLAN_STATUS_PENDING) kfree(req); LEAVE(); return ret; } /** * @brief Set/Get network monitor configurations * * @param priv A pointer to moal_private structure * @param wrq A pointer to iwreq structure * * @return 0 --success, otherwise fail */ static int woal_net_monitor_ioctl(moal_private *priv, struct iwreq *wrq) { int user_data_len = wrq->u.data.length; int data[5] = {0}, copy_len; int ret = 0; mlan_ioctl_req *req = NULL; mlan_ds_misc_cfg *misc = NULL; mlan_ds_misc_net_monitor *net_mon = NULL; mlan_status status = MLAN_STATUS_SUCCESS; ENTER(); copy_len = MIN(sizeof(data), sizeof(int) * user_data_len); req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); if (req == NULL) { LEAVE(); return -ENOMEM; } misc = (mlan_ds_misc_cfg *)req->pbuf; net_mon = (mlan_ds_misc_net_monitor *)&misc->param.net_mon; misc->sub_command = MLAN_OID_MISC_NET_MONITOR; req->req_id = MLAN_IOCTL_MISC_CFG; if (!user_data_len) { req->action = MLAN_ACT_GET; } else if (user_data_len == 1 || user_data_len == 4 || user_data_len == 5) { if (copy_from_user(data, wrq->u.data.pointer, copy_len)) { PRINTM(MERROR, "Copy from user failed\n"); ret = -EFAULT; goto done; } if (data[0] != MTRUE && data[0] != MFALSE) { PRINTM(MERROR, "NET_MON: Activity should be enable(=1)/disable(=0)\n"); ret = -EINVAL; goto done; } net_mon->enable_net_mon = data[0]; if (data[0] == MTRUE) { int i; if (user_data_len != 4 && user_data_len != 5) { PRINTM(MERROR, "NET_MON: Invalid number of args!\n"); ret = -EINVAL; goto done; } /* Supported filter flags */ if (!data[1] || data[1] & ~(MLAN_NETMON_DATA_FRAME | MLAN_NETMON_MANAGEMENT_FRAME | MLAN_NETMON_CONTROL_FRAME)) { PRINTM(MERROR, "NET_MON: Invalid filter flag\n"); ret = -EINVAL; goto done; } /* Supported bands */ for (i = 0; i < (int)sizeof(SupportedInfraBand); i++) if (data[2] == SupportedInfraBand[i]) break; if (i == sizeof(SupportedInfraBand)) { PRINTM(MERROR, "NET_MON: Invalid band\n"); ret = -EINVAL; goto done; } /* Supported channel */ if (data[3] < 1 || data[3] > MLAN_MAX_CHANNEL) { PRINTM(MERROR, "NET_MON: Invalid channel number\n"); ret = -EINVAL; goto done; } if (user_data_len == 5) { /* Secondary channel offset */ if (!(data[2] & (BAND_GN | BAND_AN))) { PRINTM(MERROR, "No 11n in band, can not set " "secondary channel offset\n"); ret = -EINVAL; goto done; } if ((data[4] != CHANNEL_BW_20MHZ) && (data[4] != CHANNEL_BW_40MHZ_ABOVE) && (data[4] != CHANNEL_BW_40MHZ_BELOW) && (data[4] != CHANNEL_BW_80MHZ)) { PRINTM(MERROR, "Invalid secondary channel bandwidth, " "only allowed 0, 1, 3 or 4\n"); ret = -EINVAL; goto done; } net_mon->chan_bandwidth = data[4]; } net_mon->filter_flag = data[1]; net_mon->band = data[2]; net_mon->channel = data[3]; } req->action = MLAN_ACT_SET; } else { PRINTM(MERROR, "NET_MON: Invalid number of args!\n"); ret = -EINVAL; goto done; } status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto done; } data[0] = net_mon->enable_net_mon; data[1] = net_mon->filter_flag; data[2] = net_mon->band; data[3] = net_mon->channel; data[4] = net_mon->chan_bandwidth; wrq->u.data.length = 5; if (copy_to_user(wrq->u.data.pointer, data, sizeof(int) * wrq->u.data.length)) { PRINTM(MERROR, "Copy to user failed\n"); ret = -EFAULT; goto done; } done: if (status != MLAN_STATUS_PENDING) kfree(req); LEAVE(); return ret; } /** * @brief Get LOG * * @param priv A pointer to moal_private structure * @param wrq A pointer to iwreq structure * * @return 0 --success, otherwise fail */ static int woal_get_log(moal_private *priv, struct iwreq *wrq) { int ret = 0; mlan_ds_get_stats stats; char *buf = NULL; int i = 0; ENTER(); PRINTM(MINFO, " GET STATS\n"); buf = kmalloc(GETLOG_BUFSIZE, GFP_KERNEL); if (!buf) { PRINTM(MERROR, "kmalloc failed!\n"); ret = -ENOMEM; goto done; } memset(&stats, 0, sizeof(mlan_ds_get_stats)); if (MLAN_STATUS_SUCCESS != woal_get_stats_info(priv, MOAL_IOCTL_WAIT, &stats)) { ret = -EFAULT; goto done; } if (wrq->u.data.pointer) { sprintf(buf, "\n" "mcasttxframe %u\n" "failed %u\n" "retry %u\n" "multiretry %u\n" "framedup %u\n" "rtssuccess %u\n" "rtsfailure %u\n" "ackfailure %u\n" "rxfrag %u\n" "mcastrxframe %u\n" "fcserror %u\n" "txframe %u\n" "wepicverrcnt-1 %u\n" "wepicverrcnt-2 %u\n" "wepicverrcnt-3 %u\n" "wepicverrcnt-4 %u\n" "beacon_rcnt %u\n" "beacon_mcnt %u\n", stats.mcast_tx_frame, stats.failed, stats.retry, stats.multi_retry, stats.frame_dup, stats.rts_success, stats.rts_failure, stats.ack_failure, stats.rx_frag, stats.mcast_rx_frame, stats.fcs_error, stats.tx_frame, stats.wep_icv_error[0], stats.wep_icv_error[1], stats.wep_icv_error[2], stats.wep_icv_error[3], stats.bcn_rcv_cnt, stats.bcn_miss_cnt); if (priv->phandle->fw_getlog_enable) { sprintf(buf + strlen(buf), "tx_frag_cnt %u\n", stats.tx_frag_cnt); sprintf(buf + strlen(buf), "qos_tx_frag_cnt "); for (i = 0; i < 8; i++) { sprintf(buf + strlen(buf), "%u ", stats.qos_tx_frag_cnt[i]); } sprintf(buf + strlen(buf), "\nqos_failed_cnt "); for (i = 0; i < 8; i++) { sprintf(buf + strlen(buf), "%u ", stats.qos_failed_cnt[i]); } sprintf(buf + strlen(buf), "\nqos_retry_cnt "); for (i = 0; i < 8; i++) { sprintf(buf + strlen(buf), "%u ", stats.qos_retry_cnt[i]); } sprintf(buf + strlen(buf), "\nqos_multi_retry_cnt "); for (i = 0; i < 8; i++) { sprintf(buf + strlen(buf), "%u ", stats.qos_multi_retry_cnt[i]); } sprintf(buf + strlen(buf), "\nqos_frm_dup_cnt "); for (i = 0; i < 8; i++) { sprintf(buf + strlen(buf), "%u ", stats.qos_frm_dup_cnt[i]); } sprintf(buf + strlen(buf), "\nqos_rts_suc_cnt "); for (i = 0; i < 8; i++) { sprintf(buf + strlen(buf), "%u ", stats.qos_rts_suc_cnt[i]); } sprintf(buf + strlen(buf), "\nqos_rts_failure_cnt "); for (i = 0; i < 8; i++) { sprintf(buf + strlen(buf), "%u ", stats.qos_rts_failure_cnt[i]); } sprintf(buf + strlen(buf), "\nqos_ack_failure_cnt "); for (i = 0; i < 8; i++) { sprintf(buf + strlen(buf), "%u ", stats.qos_ack_failure_cnt[i]); } sprintf(buf + strlen(buf), "\nqos_rx_frag_cnt "); for (i = 0; i < 8; i++) { sprintf(buf + strlen(buf), "%u ", stats.qos_rx_frag_cnt[i]); } sprintf(buf + strlen(buf), "\nqos_tx_frm_cnt "); for (i = 0; i < 8; i++) { sprintf(buf + strlen(buf), "%u ", stats.qos_tx_frm_cnt[i]); } sprintf(buf + strlen(buf), "\nqos_discarded_frm_cnt "); for (i = 0; i < 8; i++) { sprintf(buf + strlen(buf), "%u ", stats.qos_discarded_frm_cnt[i]); } sprintf(buf + strlen(buf), "\nqos_mpdus_rx_cnt "); for (i = 0; i < 8; i++) { sprintf(buf + strlen(buf), "%u ", stats.qos_mpdus_rx_cnt[i]); } sprintf(buf + strlen(buf), "\nqos_retries_rx_cnt "); for (i = 0; i < 8; i++) { sprintf(buf + strlen(buf), "%u ", stats.qos_retries_rx_cnt[i]); } sprintf(buf + strlen(buf), "\nmgmt_ccmp_replays %u\n" "tx_amsdu_cnt %u\n" "failed_amsdu_cnt %u\n" "retry_amsdu_cnt %u\n" "multi_retry_amsdu_cnt %u\n" "tx_octets_in_amsdu_cnt %llu\n" "amsdu_ack_failure_cnt %u\n" "rx_amsdu_cnt %u\n" "rx_octets_in_amsdu_cnt %llu\n" "tx_ampdu_cnt %u\n" "tx_mpdus_in_ampdu_cnt %u\n" "tx_octets_in_ampdu_cnt %llu\n" "ampdu_rx_cnt %u\n" "mpdu_in_rx_ampdu_cnt %u\n" "rx_octets_in_ampdu_cnt %llu\n" "ampdu_delimiter_crc_error_cnt %u\n", stats.mgmt_ccmp_replays, stats.tx_amsdu_cnt, stats.failed_amsdu_cnt, stats.retry_amsdu_cnt, stats.multi_retry_amsdu_cnt, stats.tx_octets_in_amsdu_cnt, stats.amsdu_ack_failure_cnt, stats.rx_amsdu_cnt, stats.rx_octets_in_amsdu_cnt, stats.tx_ampdu_cnt, stats.tx_mpdus_in_ampdu_cnt, stats.tx_octets_in_ampdu_cnt, stats.ampdu_rx_cnt, stats.mpdu_in_rx_ampdu_cnt, stats.rx_octets_in_ampdu_cnt, stats.ampdu_delimiter_crc_error_cnt); } wrq->u.data.length = MIN(GETLOG_BUFSIZE - 1, strlen(buf) + 1); if (copy_to_user(wrq->u.data.pointer, buf, wrq->u.data.length)) { PRINTM(MERROR, "Copy to user failed\n"); ret = -EFAULT; } } done: kfree(buf); LEAVE(); return ret; } /** * @brief Deauthenticate * * @param priv A pointer to moal_private structure * @param wrq A pointer to iwreq structure * * @return 0 --success, otherwise fail */ static int woal_deauth(moal_private *priv, struct iwreq *wrq) { int ret = 0; struct sockaddr saddr; ENTER(); if (wrq->u.data.length) { /* Deauth mentioned BSSID */ if (copy_from_user(&saddr, wrq->u.data.pointer, sizeof(saddr))) { PRINTM(MERROR, "Copy from user failed\n"); ret = -EFAULT; goto done; } if (MLAN_STATUS_SUCCESS != woal_disconnect(priv, MOAL_IOCTL_WAIT, (t_u8 *)saddr.sa_data, DEF_DEAUTH_REASON_CODE)) { ret = -EFAULT; goto done; } } else { if (MLAN_STATUS_SUCCESS != woal_disconnect(priv, MOAL_IOCTL_WAIT, NULL, DEF_DEAUTH_REASON_CODE)) ret = -EFAULT; } done: LEAVE(); return ret; } /** * @brief Set/Get TX power configurations * * @param priv A pointer to moal_private structure * @param wrq A pointer to iwreq structure * * @return 0 --success, otherwise fail */ static int woal_tx_power_cfg(moal_private *priv, struct iwreq *wrq) { int data[5], user_data_len, copy_len; int ret = 0; mlan_bss_info bss_info; mlan_ds_power_cfg *pcfg = NULL; mlan_ioctl_req *req = NULL; int power_data[MAX_POWER_TABLE_SIZE]; int i, power_ext_len = 0; int *ptr = power_data; mlan_power_group *pwr_grp = NULL; mlan_status status = MLAN_STATUS_SUCCESS; ENTER(); memset(&bss_info, 0, sizeof(bss_info)); woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info); memset(data, 0, sizeof(data)); user_data_len = wrq->u.data.length; copy_len = MIN(sizeof(data), sizeof(int) * user_data_len); if (user_data_len) { if (sizeof(int) * user_data_len > sizeof(data)) { PRINTM(MERROR, "Too many arguments\n"); ret = -EINVAL; goto done; } if (copy_from_user(data, wrq->u.data.pointer, copy_len)) { PRINTM(MERROR, "Copy from user failed\n"); ret = -EFAULT; goto done; } switch (user_data_len) { case 1: if (data[0] != 0xFF) ret = -EINVAL; break; case 2: case 4: if (data[0] == 0xFF) { ret = -EINVAL; break; } if (data[1] < bss_info.min_power_level) { PRINTM(MERROR, "The set powercfg rate value %d dBm is out of range (%d dBm-%d dBm)!\n", data[1], (int)bss_info.min_power_level, (int)bss_info.max_power_level); ret = -EINVAL; break; } if (user_data_len == 4) { if (data[1] > data[2]) { PRINTM(MERROR, "Min power should be less than maximum!\n"); ret = -EINVAL; break; } if (data[3] < 0) { PRINTM(MERROR, "Step should not less than 0!\n"); ret = -EINVAL; break; } if (data[2] > bss_info.max_power_level) { PRINTM(MERROR, "The set powercfg rate value %d dBm is out of range (%d dBm-%d dBm)!\n", data[2], (int)bss_info.min_power_level, (int)bss_info.max_power_level); ret = -EINVAL; break; } if (data[3] > data[2] - data[1]) { PRINTM(MERROR, "Step should not greater than power difference!\n"); ret = -EINVAL; break; } } break; default: ret = -EINVAL; break; } if (ret) goto done; } req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_power_cfg)); if (req == NULL) { ret = -ENOMEM; goto done; } pcfg = (mlan_ds_power_cfg *)req->pbuf; pcfg->sub_command = MLAN_OID_POWER_CFG_EXT; req->req_id = MLAN_IOCTL_POWER_CFG; if (!user_data_len) req->action = MLAN_ACT_GET; else { req->action = MLAN_ACT_SET; if (data[0] == 0xFF) pcfg->param.power_ext.power_group[0].rate_format = TX_PWR_CFG_AUTO_CTRL_OFF; else { pcfg->param.power_ext.power_group[0].power_step = 0; pcfg->param.power_ext.power_group[0].first_rate_ind = data[0]; pcfg->param.power_ext.power_group[0].last_rate_ind = data[0]; if (data[0] <= 11) { pcfg->param.power_ext.power_group[0] .rate_format = MLAN_RATE_FORMAT_LG; pcfg->param.power_ext.power_group[0].bandwidth = MLAN_HT_BW20; } else if (data[0] <= 27) { pcfg->param.power_ext.power_group[0] .rate_format = MLAN_RATE_FORMAT_HT; pcfg->param.power_ext.power_group[0].bandwidth = MLAN_HT_BW20; pcfg->param.power_ext.power_group[0] .first_rate_ind -= 12; pcfg->param.power_ext.power_group[0] .last_rate_ind -= 12; } else if ((140 <= data[0]) && (data[0] <= 155)) { pcfg->param.power_ext.power_group[0] .rate_format = MLAN_RATE_FORMAT_HT; pcfg->param.power_ext.power_group[0].bandwidth = MLAN_HT_BW40; pcfg->param.power_ext.power_group[0] .first_rate_ind -= 140; pcfg->param.power_ext.power_group[0] .last_rate_ind -= 140; } if (user_data_len == 2) { pcfg->param.power_ext.power_group[0].power_min = data[1]; pcfg->param.power_ext.power_group[0].power_max = data[1]; } else if (user_data_len == 4) { pcfg->param.power_ext.power_group[0].power_min = data[1]; pcfg->param.power_ext.power_group[0].power_max = data[2]; pcfg->param.power_ext.power_group[0].power_step = data[3]; } pcfg->param.power_ext.num_pwr_grp = 1; } } status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto done; } if (!user_data_len) { /* GET operation */ i = 0; power_ext_len = 0; ptr = power_data; while ((i < (int)pcfg->param.power_ext.num_pwr_grp) && ((power_ext_len + 5) < MAX_POWER_TABLE_SIZE)) { pwr_grp = &pcfg->param.power_ext.power_group[i]; if (pwr_grp->rate_format == MLAN_RATE_FORMAT_HT) { if (pwr_grp->bandwidth == MLAN_HT_BW20) { pwr_grp->first_rate_ind += 12; pwr_grp->last_rate_ind += 12; } else if (pwr_grp->bandwidth == MLAN_HT_BW40) { pwr_grp->first_rate_ind += 140; pwr_grp->last_rate_ind += 140; } } if ((pwr_grp->rate_format == MLAN_RATE_FORMAT_LG) || (pwr_grp->rate_format == MLAN_RATE_FORMAT_HT)) { *ptr = pwr_grp->first_rate_ind; ptr++; *ptr = pwr_grp->last_rate_ind; ptr++; *ptr = pwr_grp->power_min; ptr++; *ptr = pwr_grp->power_max; ptr++; *ptr = pwr_grp->power_step; ptr++; power_ext_len += 5; } i++; } if (copy_to_user(wrq->u.data.pointer, (t_u8 *)power_data, sizeof(int) * power_ext_len)) { ret = -EFAULT; goto done; } wrq->u.data.length = power_ext_len; } done: if (status != MLAN_STATUS_PENDING) kfree(req); LEAVE(); return ret; } /** * @brief Get Tx/Rx data rates * * @param priv A pointer to moal_private structure * @param wrq A pointer to iwreq structure * * @return 0 --success, otherwise fail */ static int woal_get_txrx_rate(moal_private *priv, struct iwreq *wrq) { int ret = 0; mlan_ds_rate *rate = NULL; mlan_ioctl_req *req = NULL; mlan_status status = MLAN_STATUS_SUCCESS; ENTER(); req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_rate)); if (req == NULL) { ret = -ENOMEM; goto done; } rate = (mlan_ds_rate *)req->pbuf; rate->sub_command = MLAN_OID_GET_DATA_RATE; req->req_id = MLAN_IOCTL_RATE; req->action = MLAN_ACT_GET; status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto done; } if (copy_to_user(wrq->u.data.pointer, (t_u8 *)&rate->param.data_rate, sizeof(int) * 2)) { ret = -EFAULT; goto done; } wrq->u.data.length = 2; done: if (status != MLAN_STATUS_PENDING) kfree(req); LEAVE(); return ret; } #ifdef SDIO /** * @brief Turn on/off the sdio clock * * @param priv A pointer to moal_private structure * @param wrq A pointer to iwreq structure * * @return 0/MLAN_STATUS_SUCCESS --success, otherwise fail */ static int woal_sdio_clock_ioctl(moal_private *priv, struct iwreq *wrq) { int ret = 0; int data = 2; /* Initialize the clock state as on */ static int clock_state = 1; ENTER(); if (wrq->u.data.length) { if (copy_from_user(&data, wrq->u.data.pointer, sizeof(int))) { PRINTM(MERROR, "Copy from user failed\n"); ret = -EFAULT; goto done; } } else { wrq->u.data.length = sizeof(clock_state) / sizeof(int); if (copy_to_user(wrq->u.data.pointer, &clock_state, sizeof(int))) { PRINTM(MERROR, "Copy from user failed\n"); ret = -EFAULT; goto done; } goto done; } switch (data) { case CMD_DISABLED: PRINTM(MINFO, "SDIO clock is turned off\n"); ret = woal_sdio_set_bus_clock(priv->phandle, MFALSE); clock_state = data; break; case CMD_ENABLED: PRINTM(MINFO, "SDIO clock is turned on\n"); ret = woal_sdio_set_bus_clock(priv->phandle, MTRUE); clock_state = data; break; default: ret = -EINVAL; PRINTM(MINFO, "sdioclock: wrong parameter\n"); break; } done: LEAVE(); return ret; } #endif /* SDIO */ /** * @brief Set/Get beacon interval * * @param priv A pointer to moal_private structure * @param wrq A pointer to iwreq structure * * @return 0 --success, otherwise fail */ static int woal_beacon_interval(moal_private *priv, struct iwreq *wrq) { int ret = 0; mlan_ds_bss *bss = NULL; mlan_ioctl_req *req = NULL; int bcn = 0; mlan_status status = MLAN_STATUS_SUCCESS; ENTER(); if (wrq->u.data.length) { if (copy_from_user(&bcn, wrq->u.data.pointer, sizeof(int))) { PRINTM(MERROR, "Copy from user failed\n"); ret = -EFAULT; goto done; } if ((bcn < MLAN_MIN_BEACON_INTERVAL) || (bcn > MLAN_MAX_BEACON_INTERVAL)) { ret = -EINVAL; goto done; } } req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); if (req == NULL) { ret = -ENOMEM; goto done; } bss = (mlan_ds_bss *)req->pbuf; bss->sub_command = MLAN_OID_IBSS_BCN_INTERVAL; req->req_id = MLAN_IOCTL_BSS; if (!wrq->u.data.length) req->action = MLAN_ACT_GET; else { req->action = MLAN_ACT_SET; bss->param.bcn_interval = bcn; } status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto done; } if (copy_to_user(wrq->u.data.pointer, (t_u8 *)&bss->param.bcn_interval, sizeof(int))) { ret = -EFAULT; goto done; } wrq->u.data.length = 1; done: if (status != MLAN_STATUS_PENDING) kfree(req); LEAVE(); return ret; } /** * @brief Set/Get TX data rate * * @param priv A pointer to moal_private structure * @param wrq A pointer to iwreq structure * * @return 0 --success, otherwise fail */ static int woal_set_get_txrate(moal_private *priv, struct iwreq *wrq) { int ret = 0; mlan_ds_rate *rate = NULL; mlan_ioctl_req *req = NULL; int rateindex = 0; mlan_status status = MLAN_STATUS_SUCCESS; ENTER(); if (wrq->u.data.length) { if (copy_from_user(&rateindex, wrq->u.data.pointer, sizeof(int))) { PRINTM(MERROR, "Copy from user failed\n"); ret = -EFAULT; goto done; } } req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_rate)); if (req == NULL) { ret = -ENOMEM; goto done; } rate = (mlan_ds_rate *)req->pbuf; rate->param.rate_cfg.rate_type = MLAN_RATE_INDEX; rate->sub_command = MLAN_OID_RATE_CFG; req->req_id = MLAN_IOCTL_RATE; if (!wrq->u.data.length) req->action = MLAN_ACT_GET; else { req->action = MLAN_ACT_SET; if (rateindex == AUTO_RATE) rate->param.rate_cfg.is_rate_auto = 1; else { if ((rateindex != MLAN_RATE_INDEX_MCS32) && ((rateindex < 0) || (rateindex > MLAN_RATE_INDEX_MCS15))) { ret = -EINVAL; goto done; } } rate->param.rate_cfg.rate = rateindex; } status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto done; } else { if (wrq->u.data.length) priv->rate_index = rateindex; } if (!wrq->u.data.length) { if (rate->param.rate_cfg.is_rate_auto) rateindex = AUTO_RATE; else rateindex = rate->param.rate_cfg.rate; wrq->u.data.length = 1; if (copy_to_user(wrq->u.data.pointer, &rateindex, sizeof(int))) ret = -EFAULT; } done: if (status != MLAN_STATUS_PENDING) kfree(req); LEAVE(); return ret; } /** * @brief Set/Get region code * * @param priv A pointer to moal_private structure * @param wrq A pointer to iwreq structure * * @return 0 --success, otherwise fail */ static int woal_set_get_regioncode(moal_private *priv, struct iwreq *wrq) { int ret = 0; mlan_ds_misc_cfg *cfg = NULL; mlan_ioctl_req *req = NULL; int region = 0; mlan_status status = MLAN_STATUS_SUCCESS; ENTER(); if (wrq->u.data.length) { if (copy_from_user(®ion, wrq->u.data.pointer, sizeof(int))) { PRINTM(MERROR, "Copy from user failed\n"); ret = -EFAULT; goto done; } } req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); if (req == NULL) { ret = -ENOMEM; goto done; } cfg = (mlan_ds_misc_cfg *)req->pbuf; cfg->sub_command = MLAN_OID_MISC_REGION; req->req_id = MLAN_IOCTL_MISC_CFG; if (!wrq->u.data.length) req->action = MLAN_ACT_GET; else { req->action = MLAN_ACT_SET; cfg->param.region_code = region; } status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto done; } if (!wrq->u.data.length) { wrq->u.data.length = 1; if (copy_to_user(wrq->u.data.pointer, &cfg->param.region_code, sizeof(int))) ret = -EFAULT; } done: if (status != MLAN_STATUS_PENDING) kfree(req); LEAVE(); return ret; } /** * @brief Set/Get radio * * @param priv A pointer to moal_private structure * @param wrq A pointer to iwreq structure * * @return 0 --success, otherwise fail */ static int woal_set_get_radio(moal_private *priv, struct iwreq *wrq) { int ret = 0; mlan_bss_info bss_info; int option = 0; ENTER(); memset(&bss_info, 0, sizeof(bss_info)); if (wrq->u.data.length) { /* Set radio */ if (copy_from_user(&option, wrq->u.data.pointer, sizeof(int))) { PRINTM(MERROR, "Copy from user failed\n"); ret = -EFAULT; goto done; } if (MLAN_STATUS_SUCCESS != woal_set_radio(priv, (t_u8)option)) ret = -EFAULT; } else { /* Get radio status */ woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info); wrq->u.data.length = 1; if (copy_to_user(wrq->u.data.pointer, &bss_info.radio_on, sizeof(bss_info.radio_on))) { PRINTM(MERROR, "Copy to user failed\n"); ret = -EFAULT; } } done: LEAVE(); return ret; } #ifdef DEBUG_LEVEL1 /** * @brief Get/Set the bit mask of driver debug message control * * @param priv A pointer to moal_private structure * @param wrq A pointer to wrq structure * * @return 0 --success, otherwise fail */ static int woal_drv_dbg(moal_private *priv, struct iwreq *wrq) { int data[4], copy_len; int ret = 0; int data_length = wrq->u.data.length; ENTER(); copy_len = MIN(sizeof(data), sizeof(int) * data_length); if (!data_length) { data[0] = drvdbg; /* Return the current driver debug bit masks */ if (copy_to_user(wrq->u.data.pointer, data, sizeof(int))) { PRINTM(MERROR, "Copy to user failed\n"); ret = -EFAULT; goto drvdbgexit; } wrq->u.data.length = 1; } else if (data_length < 3) { /* Get the driver debug bit masks from user */ if (copy_from_user(data, wrq->u.data.pointer, copy_len)) { PRINTM(MERROR, "Copy from user failed\n"); ret = -EFAULT; goto drvdbgexit; } drvdbg = data[0]; /* Set the driver debug bit masks into mlan */ woal_set_drvdbg(priv, drvdbg); } else { PRINTM(MERROR, "Invalid parameter number\n"); goto drvdbgexit; } printk(KERN_ALERT "drvdbg = 0x%08x\n", drvdbg); #ifdef DEBUG_LEVEL2 printk(KERN_ALERT "MINFO (%08x) %s\n", MINFO, (drvdbg & MINFO) ? "X" : ""); printk(KERN_ALERT "MWARN (%08x) %s\n", MWARN, (drvdbg & MWARN) ? "X" : ""); printk(KERN_ALERT "MENTRY (%08x) %s\n", MENTRY, (drvdbg & MENTRY) ? "X" : ""); #endif printk(KERN_ALERT "MMPA_D (%08x) %s\n", MMPA_D, (drvdbg & MMPA_D) ? "X" : ""); printk(KERN_ALERT "MIF_D (%08x) %s\n", MIF_D, (drvdbg & MIF_D) ? "X" : ""); printk(KERN_ALERT "MFW_D (%08x) %s\n", MFW_D, (drvdbg & MFW_D) ? "X" : ""); printk(KERN_ALERT "MEVT_D (%08x) %s\n", MEVT_D, (drvdbg & MEVT_D) ? "X" : ""); printk(KERN_ALERT "MCMD_D (%08x) %s\n", MCMD_D, (drvdbg & MCMD_D) ? "X" : ""); printk(KERN_ALERT "MDAT_D (%08x) %s\n", MDAT_D, (drvdbg & MDAT_D) ? "X" : ""); printk(KERN_ALERT "MREG_D (%08x) %s\n", MREG_D, (drvdbg & MREG_D) ? "X" : ""); printk(KERN_ALERT "MIOCTL (%08x) %s\n", MIOCTL, (drvdbg & MIOCTL) ? "X" : ""); printk(KERN_ALERT "MINTR (%08x) %s\n", MINTR, (drvdbg & MINTR) ? "X" : ""); printk(KERN_ALERT "MEVENT (%08x) %s\n", MEVENT, (drvdbg & MEVENT) ? "X" : ""); printk(KERN_ALERT "MCMND (%08x) %s\n", MCMND, (drvdbg & MCMND) ? "X" : ""); printk(KERN_ALERT "MDATA (%08x) %s\n", MDATA, (drvdbg & MDATA) ? "X" : ""); printk(KERN_ALERT "MERROR (%08x) %s\n", MERROR, (drvdbg & MERROR) ? "X" : ""); printk(KERN_ALERT "MFATAL (%08x) %s\n", MFATAL, (drvdbg & MFATAL) ? "X" : ""); printk(KERN_ALERT "MMSG (%08x) %s\n", MMSG, (drvdbg & MMSG) ? "X" : ""); drvdbgexit: LEAVE(); return ret; } #endif /* DEBUG_LEVEL1 */ /** * @brief Set/Get QoS configuration * * @param priv A pointer to moal_private structure * @param wrq A pointer to iwreq structure * * @return 0 --success, otherwise fail */ static int woal_set_get_qos_cfg(moal_private *priv, struct iwreq *wrq) { int ret = 0; mlan_ds_wmm_cfg *cfg = NULL; mlan_ioctl_req *req = NULL; int data = 0; mlan_status status = MLAN_STATUS_SUCCESS; ENTER(); req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_wmm_cfg)); if (req == NULL) { ret = -ENOMEM; goto done; } cfg = (mlan_ds_wmm_cfg *)req->pbuf; cfg->sub_command = MLAN_OID_WMM_CFG_QOS; req->req_id = MLAN_IOCTL_WMM_CFG; if (wrq->u.data.length) { if (copy_from_user(&data, wrq->u.data.pointer, sizeof(int))) { PRINTM(MERROR, "Copy from user failed\n"); ret = -EFAULT; goto done; } req->action = MLAN_ACT_SET; cfg->param.qos_cfg = (t_u8)data; } else req->action = MLAN_ACT_GET; status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto done; } if (!wrq->u.data.length) { data = (int)cfg->param.qos_cfg; if (copy_to_user(wrq->u.data.pointer, &data, sizeof(int))) { ret = -EFAULT; goto done; } wrq->u.data.length = 1; } done: if (status != MLAN_STATUS_PENDING) kfree(req); LEAVE(); return ret; } #ifndef OPCHAN /** * @brief Set/Get WWS mode * * @param priv A pointer to moal_private structure * @param wrq A pointer to iwreq structure * * @return 0 --success, otherwise fail */ static int woal_wws_cfg(moal_private *priv, struct iwreq *wrq) { int ret = 0; mlan_ds_misc_cfg *wws = NULL; mlan_ioctl_req *req = NULL; int data = 0; mlan_status status = MLAN_STATUS_SUCCESS; ENTER(); req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); if (req == NULL) { ret = -ENOMEM; goto done; } wws = (mlan_ds_misc_cfg *)req->pbuf; wws->sub_command = MLAN_OID_MISC_WWS; req->req_id = MLAN_IOCTL_MISC_CFG; if (wrq->u.data.length) { if (copy_from_user(&data, wrq->u.data.pointer, sizeof(int))) { PRINTM(MERROR, "Copy from user failed\n"); ret = -EFAULT; goto done; } if (data != CMD_DISABLED && data != CMD_ENABLED) { ret = -EINVAL; goto done; } req->action = MLAN_ACT_SET; wws->param.wws_cfg = data; } else req->action = MLAN_ACT_GET; status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto done; } if (!wrq->u.data.length) { data = wws->param.wws_cfg; if (copy_to_user(wrq->u.data.pointer, &data, sizeof(int))) { ret = -EFAULT; goto done; } wrq->u.data.length = 1; } done: if (status != MLAN_STATUS_PENDING) kfree(req); LEAVE(); return ret; } #endif /** * @brief Set/Get sleep period * * @param priv A pointer to moal_private structure * @param wrq A pointer to iwreq structure * * @return 0 --success, otherwise fail */ static int woal_sleep_pd(moal_private *priv, struct iwreq *wrq) { int ret = 0; mlan_ds_pm_cfg *pm_cfg = NULL; mlan_ioctl_req *req = NULL; int data = 0; mlan_status status = MLAN_STATUS_SUCCESS; ENTER(); req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_pm_cfg)); if (req == NULL) { ret = -ENOMEM; goto done; } pm_cfg = (mlan_ds_pm_cfg *)req->pbuf; pm_cfg->sub_command = MLAN_OID_PM_CFG_SLEEP_PD; req->req_id = MLAN_IOCTL_PM_CFG; if (wrq->u.data.length) { if (copy_from_user(&data, wrq->u.data.pointer, sizeof(int))) { PRINTM(MERROR, "Copy from user failed\n"); ret = -EFAULT; goto done; } if ((data <= MAX_SLEEP_PERIOD && data >= MIN_SLEEP_PERIOD) || (data == 0) || (data == SLEEP_PERIOD_RESERVED_FF)) { req->action = MLAN_ACT_SET; pm_cfg->param.sleep_period = data; } else { ret = -EINVAL; goto done; } } else req->action = MLAN_ACT_GET; status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto done; } if (!wrq->u.data.length) { data = pm_cfg->param.sleep_period; if (copy_to_user(wrq->u.data.pointer, &data, sizeof(int))) { ret = -EFAULT; goto done; } wrq->u.data.length = 1; } done: if (status != MLAN_STATUS_PENDING) kfree(req); LEAVE(); return ret; } /** * @brief Configure sleep parameters * * @param priv A pointer to moal_private structure * @param req A pointer to ifreq structure * * @return 0 --success, otherwise fail */ static int woal_sleep_params_ioctl(moal_private *priv, struct iwreq *wrq) { int ret = 0; mlan_ioctl_req *req = NULL; mlan_ds_pm_cfg *pm = NULL; mlan_ds_sleep_params *psleep_params = NULL; int data[6] = {0}, i, copy_len; int data_length = wrq->u.data.length; #ifdef DEBUG_LEVEL1 char err_str[][36] = {{"sleep clock error in ppm"}, {"wakeup offset in usec"}, {"clock stabilization time in usec"}, {"control periodic calibration(0-2)"}, {"control of external sleepClock(0-2)"}, {"value of reserved for debug"}}; #endif mlan_status status = MLAN_STATUS_SUCCESS; ENTER(); copy_len = MIN(sizeof(data), sizeof(int) * data_length); req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_pm_cfg)); if (req == NULL) { LEAVE(); return -ENOMEM; } pm = (mlan_ds_pm_cfg *)req->pbuf; pm->sub_command = MLAN_OID_PM_CFG_SLEEP_PARAMS; req->req_id = MLAN_IOCTL_PM_CFG; psleep_params = (pmlan_ds_sleep_params)&pm->param.sleep_params; if (data_length == 0) { req->action = MLAN_ACT_GET; } else if (data_length == 6) { if (copy_from_user(data, wrq->u.data.pointer, copy_len)) { /* copy_from_user failed */ PRINTM(MERROR, "S_PARAMS: copy from user failed\n"); ret = -EINVAL; goto done; } #define MIN_VAL 0x0000 #define MAX_VAL 0xFFFF for (i = 0; i < 6; i++) { if ((i == 3) || (i == 4)) { /* These two cases are handled below the loop */ continue; } if (data[i] < MIN_VAL || data[i] > MAX_VAL) { PRINTM(MERROR, "Invalid %s (0-65535)!\n", err_str[i]); ret = -EINVAL; goto done; } } if (data[3] < 0 || data[3] > 2) { PRINTM(MERROR, "Invalid control periodic calibration (0-2)!\n"); ret = -EINVAL; goto done; } if (data[4] < 0 || data[4] > 2) { PRINTM(MERROR, "Invalid control of external sleep clock (0-2)!\n"); ret = -EINVAL; goto done; } req->action = MLAN_ACT_SET; psleep_params->error = data[0]; psleep_params->offset = data[1]; psleep_params->stable_time = data[2]; psleep_params->cal_control = data[3]; psleep_params->ext_sleep_clk = data[4]; psleep_params->reserved = data[5]; } else { ret = -EINVAL; goto done; } status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto done; } data[0] = psleep_params->error; data[1] = psleep_params->offset; data[2] = psleep_params->stable_time; data[3] = psleep_params->cal_control; data[4] = psleep_params->ext_sleep_clk; data[5] = psleep_params->reserved; wrq->u.data.length = 6; if (copy_to_user(wrq->u.data.pointer, data, sizeof(int) * wrq->u.data.length)) { PRINTM(MERROR, "QCONFIG: copy to user failed\n"); ret = -EFAULT; goto done; } done: if (status != MLAN_STATUS_PENDING) kfree(req); LEAVE(); return ret; } /** * @brief Set/get user provisioned local power constraint * * @param priv A pointer to moal_private structure * @param wrq A pointer to iwreq structure * @return 0 --success, otherwise fail */ static int woal_set_get_11h_local_pwr_constraint(moal_private *priv, struct iwreq *wrq) { int ret = 0, data = 0; mlan_ioctl_req *req = NULL; mlan_ds_11h_cfg *ds_11hcfg = NULL; mlan_status status = MLAN_STATUS_SUCCESS; ENTER(); req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11h_cfg)); if (req == NULL) { ret = -ENOMEM; goto done; } ds_11hcfg = (mlan_ds_11h_cfg *)req->pbuf; if (wrq->u.data.length) { if (copy_from_user(&data, wrq->u.data.pointer, sizeof(int))) { PRINTM(MINFO, "Copy from user failed\n"); ret = -EFAULT; goto done; } ds_11hcfg->param.usr_local_power_constraint = (t_s8)data; req->action = MLAN_ACT_SET; } else req->action = MLAN_ACT_GET; ds_11hcfg->sub_command = MLAN_OID_11H_LOCAL_POWER_CONSTRAINT; req->req_id = MLAN_IOCTL_11H_CFG; status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto done; } /* Copy response to user */ if (req->action == MLAN_ACT_GET) { data = (int)ds_11hcfg->param.usr_local_power_constraint; if (copy_to_user(wrq->u.data.pointer, &data, sizeof(int))) { PRINTM(MINFO, "Copy to user failed\n"); ret = -EFAULT; goto done; } wrq->u.data.length = 1; } done: if (status != MLAN_STATUS_PENDING) kfree(req); LEAVE(); return ret; } /** * @brief Set/get HT stream configurations * * @param priv A pointer to moal_private structure * @param wrq A pointer to iwreq structure * @return 0 --success, otherwise fail */ static int woal_ht_stream_cfg_ioctl(moal_private *priv, struct iwreq *wrq) { int ret = 0, data = 0; mlan_ioctl_req *req = NULL; mlan_ds_11n_cfg *cfg = NULL; mlan_status status = MLAN_STATUS_SUCCESS; ENTER(); req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); if (req == NULL) { ret = -ENOMEM; goto done; } cfg = (mlan_ds_11n_cfg *)req->pbuf; if (wrq->u.data.length) { if (copy_from_user(&data, wrq->u.data.pointer, sizeof(int))) { PRINTM(MINFO, "Copy from user failed\n"); ret = -EFAULT; goto done; } if (data != HT_STREAM_MODE_1X1 && data != HT_STREAM_MODE_2X2) { PRINTM(MINFO, "Invalid argument\n"); ret = -EINVAL; goto done; } cfg->param.stream_cfg = data; req->action = MLAN_ACT_SET; } else req->action = MLAN_ACT_GET; cfg->sub_command = MLAN_OID_11N_CFG_STREAM_CFG; req->req_id = MLAN_IOCTL_11N_CFG; status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto done; } /* Copy response to user */ data = (int)cfg->param.stream_cfg; if (copy_to_user(wrq->u.data.pointer, &data, sizeof(int))) { PRINTM(MINFO, "Copy to user failed\n"); ret = -EFAULT; goto done; } wrq->u.data.length = 1; done: if (status != MLAN_STATUS_PENDING) kfree(req); LEAVE(); return ret; } /** * @brief Set/get MAC control configuration * * @param priv A pointer to moal_private structure * @param wrq A pointer to iwreq structure * @return 0 --success, otherwise fail */ static int woal_mac_control_ioctl(moal_private *priv, struct iwreq *wrq) { int ret = 0, data = 0; mlan_ioctl_req *req = NULL; mlan_ds_misc_cfg *cfg = NULL; mlan_status status = MLAN_STATUS_SUCCESS; ENTER(); req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); if (req == NULL) { ret = -ENOMEM; goto done; } cfg = (mlan_ds_misc_cfg *)req->pbuf; if (wrq->u.data.length) { if (copy_from_user(&data, wrq->u.data.pointer, sizeof(int))) { PRINTM(MINFO, "Copy from user failed\n"); ret = -EFAULT; goto done; } /* Validation will be done later */ cfg->param.mac_ctrl = data; req->action = MLAN_ACT_SET; } else req->action = MLAN_ACT_GET; cfg->sub_command = MLAN_OID_MISC_MAC_CONTROL; req->req_id = MLAN_IOCTL_MISC_CFG; status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto done; } /* Copy response to user */ data = (int)cfg->param.mac_ctrl; if (copy_to_user(wrq->u.data.pointer, &data, sizeof(int))) { PRINTM(MINFO, "Copy to user failed\n"); ret = -EFAULT; goto done; } wrq->u.data.length = 1; done: if (status != MLAN_STATUS_PENDING) kfree(req); LEAVE(); return ret; } /** * @brief Get thermal reading * * @param priv A pointer to moal_private structure * @param wrq A pointer to iwreq structure * @return 0 --success, otherwise fail */ static int woal_thermal_ioctl(moal_private *priv, struct iwreq *wrq) { int ret = 0, data = 0; mlan_ioctl_req *req = NULL; mlan_ds_misc_cfg *cfg = NULL; mlan_status status = MLAN_STATUS_SUCCESS; ENTER(); req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); if (req == NULL) { ret = -ENOMEM; goto done; } cfg = (mlan_ds_misc_cfg *)req->pbuf; if (wrq->u.data.length) { PRINTM(MERROR, "Set is not supported for this command\n"); ret = -EINVAL; goto done; } req->action = MLAN_ACT_GET; cfg->sub_command = MLAN_OID_MISC_THERMAL; req->req_id = MLAN_IOCTL_MISC_CFG; status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto done; } /* Copy response to user */ data = (int)cfg->param.thermal; if (copy_to_user(wrq->u.data.pointer, &data, sizeof(int))) { PRINTM(MINFO, "Copy to user failed\n"); ret = -EFAULT; goto done; } wrq->u.data.length = 1; done: if (status != MLAN_STATUS_PENDING) kfree(req); LEAVE(); return ret; } /** * @brief Set/get hotspot enable state * * @param priv Pointer to the moal_private driver data struct * @param wrq Pointer to user data * * @return 0 --success, otherwise fail */ static int woal_cfg_hotspot(moal_private *priv, struct iwreq *wrq) { int ret = 0; mlan_ioctl_req *req = NULL; mlan_ds_misc_cfg *cfg = NULL; int config; mlan_status status = MLAN_STATUS_SUCCESS; ENTER(); if (wrq->u.data.length > 1) { PRINTM(MERROR, "Invalid no of arguments!\n"); ret = -EINVAL; goto done; } req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); if (req == NULL) { ret = -ENOMEM; goto done; } cfg = (mlan_ds_misc_cfg *)req->pbuf; if (wrq->u.data.length == 0) req->action = MLAN_ACT_GET; else { if (copy_from_user(&config, wrq->u.data.pointer, sizeof(int))) { PRINTM(MERROR, "copy from user failed\n"); ret = -EFAULT; goto done; } cfg->param.hotspot_cfg = config; req->action = MLAN_ACT_SET; } cfg->sub_command = MLAN_OID_MISC_HOTSPOT_CFG; req->req_id = MLAN_IOCTL_MISC_CFG; status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto done; } config = cfg->param.hotspot_cfg; if (copy_to_user(wrq->u.data.pointer, &config, sizeof(int))) { ret = -EFAULT; goto done; } wrq->u.data.length = 1; done: if (status != MLAN_STATUS_PENDING) kfree(req); LEAVE(); return ret; } #if defined(REASSOCIATION) /** * @brief Set/Get reassociation settings * * @param priv A pointer to moal_private structure * @param wrq A pointer to iwreq structure * * @return 0 --success, otherwise fail */ static int woal_set_get_reassoc(moal_private *priv, struct iwreq *wrq) { moal_handle *handle = priv->phandle; int ret = 0; int data = 0; ENTER(); if (wrq->u.data.length) { if (copy_from_user(&data, wrq->u.data.pointer, sizeof(int))) { PRINTM(MERROR, "Copy from user failed\n"); ret = -EFAULT; goto done; } if (data == 0) { handle->reassoc_on &= ~MBIT(priv->bss_index); priv->reassoc_on = MFALSE; priv->reassoc_required = MFALSE; if (!handle->reassoc_on && handle->is_reassoc_timer_set == MTRUE) { woal_cancel_timer(&handle->reassoc_timer); handle->is_reassoc_timer_set = MFALSE; } } else { handle->reassoc_on |= MBIT(priv->bss_index); priv->reassoc_on = MTRUE; } } else { data = (int)(priv->reassoc_on); if (copy_to_user(wrq->u.data.pointer, &data, sizeof(int))) { ret = -EFAULT; goto done; } wrq->u.data.length = 1; } done: LEAVE(); return ret; } #endif /* REASSOCIATION */ /** * @brief implement WMM enable command * * @param priv Pointer to the moal_private driver data struct * @param wrq Pointer to user data * * @return 0 --success, otherwise fail */ static int woal_wmm_enable_ioctl(moal_private *priv, struct iwreq *wrq) { int ret = 0; mlan_ds_wmm_cfg *wmm = NULL; mlan_ioctl_req *req = NULL; int data = 0; mlan_status status = MLAN_STATUS_SUCCESS; ENTER(); req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_wmm_cfg)); if (req == NULL) { ret = -ENOMEM; goto done; } wmm = (mlan_ds_wmm_cfg *)req->pbuf; req->req_id = MLAN_IOCTL_WMM_CFG; wmm->sub_command = MLAN_OID_WMM_CFG_ENABLE; if (wrq->u.data.length) { /* Set WMM configuration */ if (copy_from_user(&data, wrq->u.data.pointer, sizeof(int))) { PRINTM(MERROR, "Copy from user failed\n"); ret = -EFAULT; goto done; } if ((data < CMD_DISABLED) || (data > CMD_ENABLED)) { ret = -EINVAL; goto done; } req->action = MLAN_ACT_SET; if (data == CMD_DISABLED) wmm->param.wmm_enable = MFALSE; else wmm->param.wmm_enable = MTRUE; } else { /* Get WMM status */ req->action = MLAN_ACT_GET; } status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto done; } if (wrq->u.data.pointer) { if (copy_to_user(wrq->u.data.pointer, &wmm->param.wmm_enable, sizeof(wmm->param.wmm_enable))) { PRINTM(MERROR, "Copy to user failed\n"); ret = -EFAULT; goto done; } wrq->u.data.length = 1; } done: if (status != MLAN_STATUS_PENDING) kfree(req); LEAVE(); return ret; } /** * @brief Implement 802.11D enable command * * @param priv Pointer to the moal_private driver data struct * @param wrq Pointer to user data * * @return 0 --success, otherwise fail */ static int woal_11d_enable_ioctl(moal_private *priv, struct iwreq *wrq) { int ret = 0; mlan_ds_11d_cfg *pcfg_11d = NULL; mlan_ioctl_req *req = NULL; int data = 0; mlan_status status = MLAN_STATUS_SUCCESS; ENTER(); req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11d_cfg)); if (req == NULL) { ret = -ENOMEM; goto done; } pcfg_11d = (mlan_ds_11d_cfg *)req->pbuf; req->req_id = MLAN_IOCTL_11D_CFG; pcfg_11d->sub_command = MLAN_OID_11D_CFG_ENABLE; if (wrq->u.data.length) { /* Set 11D configuration */ if (copy_from_user(&data, wrq->u.data.pointer, sizeof(int))) { PRINTM(MERROR, "Copy from user failed\n"); ret = -EFAULT; goto done; } if ((data < CMD_DISABLED) || (data > CMD_ENABLED)) { ret = -EINVAL; goto done; } if (data == CMD_DISABLED) pcfg_11d->param.enable_11d = MFALSE; else pcfg_11d->param.enable_11d = MTRUE; req->action = MLAN_ACT_SET; } else { req->action = MLAN_ACT_GET; } status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto done; } if (wrq->u.data.pointer) { if (copy_to_user(wrq->u.data.pointer, &pcfg_11d->param.enable_11d, sizeof(pcfg_11d->param.enable_11d))) { PRINTM(MERROR, "Copy to user failed\n"); ret = -EFAULT; goto done; } wrq->u.data.length = 1; } done: if (status != MLAN_STATUS_PENDING) kfree(req); LEAVE(); return ret; } /** * @brief Implement 802.11D clear chan table command * * @param priv Pointer to the moal_private driver data struct * @param wrq Pointer to user data * * @return 0 --success, otherwise fail */ static int woal_11d_clr_chan_table(moal_private *priv, struct iwreq *wrq) { int ret = 0; mlan_ds_11d_cfg *pcfg_11d = NULL; mlan_ioctl_req *req = NULL; mlan_status status = MLAN_STATUS_SUCCESS; ENTER(); req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11d_cfg)); if (req == NULL) { ret = -ENOMEM; goto done; } pcfg_11d = (mlan_ds_11d_cfg *)req->pbuf; req->req_id = MLAN_IOCTL_11D_CFG; pcfg_11d->sub_command = MLAN_OID_11D_CLR_CHAN_TABLE; req->action = MLAN_ACT_SET; status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto done; } done: if (status != MLAN_STATUS_PENDING) kfree(req); LEAVE(); return ret; } /** * @brief Control WPS Session Enable/Disable * * @param priv Pointer to the moal_private driver data struct * @param wrq Pointer to user data * * @return 0 --success, otherwise fail */ static int woal_wps_cfg_ioctl(moal_private *priv, struct iwreq *wrq) { int ret = 0; mlan_ds_wps_cfg *pwps = NULL; mlan_ioctl_req *req = NULL; char buf[8]; struct iwreq *wreq = (struct iwreq *)wrq; mlan_status status = MLAN_STATUS_SUCCESS; ENTER(); PRINTM(MINFO, "WOAL_WPS_SESSION\n"); memset(buf, 0, sizeof(buf)); if (copy_from_user(buf, wreq->u.data.pointer, MIN(sizeof(buf) - 1, wreq->u.data.length))) { PRINTM(MERROR, "Copy from user failed\n"); ret = -EFAULT; goto done; } req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_wps_cfg)); if (req == NULL) { ret = -ENOMEM; goto done; } pwps = (mlan_ds_wps_cfg *)req->pbuf; req->req_id = MLAN_IOCTL_WPS_CFG; req->action = MLAN_ACT_SET; pwps->sub_command = MLAN_OID_WPS_CFG_SESSION; if (buf[0] == 1) pwps->param.wps_session = MLAN_WPS_CFG_SESSION_START; else pwps->param.wps_session = MLAN_WPS_CFG_SESSION_END; status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto done; } done: if (status != MLAN_STATUS_PENDING) kfree(req); LEAVE(); return ret; } /** * @brief Set WPA passphrase and SSID * * @param priv A pointer to moal_private structure * @param wrq A pointer to user data * * @return 0 --success, otherwise fail */ static int woal_passphrase(moal_private *priv, struct iwreq *wrq) { t_u16 len = 0; char buf[256]; char *begin = NULL, *end = NULL, *opt = NULL; int ret = 0, action = -1, i; mlan_ds_sec_cfg *sec = NULL; mlan_ioctl_req *req = NULL; t_u8 zero_mac[] = {0, 0, 0, 0, 0, 0}; t_u8 *mac = NULL; int data_length = wrq->u.data.length, copy_len; mlan_status status = MLAN_STATUS_SUCCESS; ENTER(); if (!priv->phandle->card_info->embedded_supp) { PRINTM(MERROR, "Not supported cmd on this card\n"); ret = -EOPNOTSUPP; goto done; } if (!data_length || data_length >= (int)sizeof(buf) - 1) { PRINTM(MERROR, "Argument missing or too long for setpassphrase\n"); ret = -EINVAL; goto done; } memset(buf, 0, sizeof(buf)); copy_len = data_length; if (copy_from_user(buf, wrq->u.data.pointer, copy_len)) { PRINTM(MERROR, "Copy from user failed\n"); ret = -EFAULT; goto done; } buf[copy_len] = '\0'; /* Parse the buf to get the cmd_action */ begin = buf; end = woal_strsep(&begin, ';', '/'); if (!end) { PRINTM(MERROR, "Invalid option\n"); ret = -EINVAL; goto done; } action = woal_atox(end); if (action < 0 || action > 2 || end[1] != '\0') { PRINTM(MERROR, "Invalid action argument %s\n", end); ret = -EINVAL; goto done; } req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg)); if (req == NULL) { ret = -ENOMEM; goto done; } sec = (mlan_ds_sec_cfg *)req->pbuf; sec->sub_command = MLAN_OID_SEC_CFG_PASSPHRASE; req->req_id = MLAN_IOCTL_SEC_CFG; if (action == 0) req->action = MLAN_ACT_GET; else req->action = MLAN_ACT_SET; while (begin) { end = woal_strsep(&begin, ';', '/'); opt = woal_strsep(&end, '=', '/'); if (!opt || !end || !end[0]) { PRINTM(MERROR, "Invalid option\n"); ret = -EINVAL; break; } else if (!strnicmp(opt, "ssid", strlen(opt))) { if (strlen(end) > MLAN_MAX_SSID_LENGTH) { PRINTM(MERROR, "SSID length exceeds max length\n"); ret = -EFAULT; break; } sec->param.passphrase.ssid.ssid_len = strlen(end); moal_memcpy_ext(priv->phandle, sec->param.passphrase.ssid.ssid, end, strlen(end), MLAN_MAX_SSID_LENGTH); PRINTM(MINFO, "ssid=%s, len=%d\n", sec->param.passphrase.ssid.ssid, (int)sec->param.passphrase.ssid.ssid_len); } else if (!strnicmp(opt, "bssid", strlen(opt))) { woal_mac2u8(sec->param.passphrase.bssid, end); } else if (!strnicmp(opt, "psk", strlen(opt)) && req->action == MLAN_ACT_SET) { if (strlen(end) != MLAN_PMK_HEXSTR_LENGTH) { PRINTM(MERROR, "Invalid PMK length\n"); ret = -EINVAL; break; } woal_ascii2hex( (t_u8 *)(sec->param.passphrase.psk.pmk.pmk), end, MLAN_PMK_HEXSTR_LENGTH / 2); sec->param.passphrase.psk_type = MLAN_PSK_PMK; } else if (!strnicmp(opt, "passphrase", strlen(opt)) && req->action == MLAN_ACT_SET) { if (strlen(end) < MLAN_MIN_PASSPHRASE_LENGTH || strlen(end) > MLAN_MAX_PASSPHRASE_LENGTH) { PRINTM(MERROR, "Invalid length for passphrase\n"); ret = -EINVAL; break; } sec->param.passphrase.psk_type = MLAN_PSK_PASSPHRASE; moal_memcpy_ext( priv->phandle, sec->param.passphrase.psk.passphrase.passphrase, end, sizeof(sec->param.passphrase.psk.passphrase .passphrase), sizeof(sec->param.passphrase.psk.passphrase .passphrase)); sec->param.passphrase.psk.passphrase.passphrase_len = strlen(end); PRINTM(MINFO, "passphrase=%s, len=%d\n", sec->param.passphrase.psk.passphrase.passphrase, (int)sec->param.passphrase.psk.passphrase .passphrase_len); } else { PRINTM(MERROR, "Invalid option %s\n", opt); ret = -EINVAL; break; } } if (ret) goto done; if (action == 2) sec->param.passphrase.psk_type = MLAN_PSK_CLEAR; else if (action == 0) sec->param.passphrase.psk_type = MLAN_PSK_QUERY; status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto done; } if (action == 0) { memset(buf, 0, sizeof(buf)); if (sec->param.passphrase.ssid.ssid_len) { len += sprintf(buf + len, "ssid:"); moal_memcpy_ext(priv->phandle, buf + len, sec->param.passphrase.ssid.ssid, sec->param.passphrase.ssid.ssid_len, sizeof(buf) - len); len += sec->param.passphrase.ssid.ssid_len; len += sprintf(buf + len, " "); } if (memcmp(&sec->param.passphrase.bssid, zero_mac, sizeof(zero_mac))) { mac = (t_u8 *)&sec->param.passphrase.bssid; len += sprintf(buf + len, "bssid:"); for (i = 0; i < ETH_ALEN - 1; ++i) len += sprintf(buf + len, "%02x:", mac[i]); len += sprintf(buf + len, "%02x ", mac[i]); } if (sec->param.passphrase.psk_type == MLAN_PSK_PMK) { len += sprintf(buf + len, "psk:"); for (i = 0; i < MLAN_MAX_KEY_LENGTH; ++i) len += sprintf( buf + len, "%02x", sec->param.passphrase.psk.pmk.pmk[i]); len += sprintf(buf + len, "\n"); } if (sec->param.passphrase.psk_type == MLAN_PSK_PASSPHRASE) { len += sprintf( buf + len, "passphrase:%s\n", sec->param.passphrase.psk.passphrase.passphrase); } if (wrq->u.data.pointer) { if (copy_to_user(wrq->u.data.pointer, buf, MIN(len, sizeof(buf)))) { PRINTM(MERROR, "Copy to user failed, len %d\n", len); ret = -EFAULT; goto done; } wrq->u.data.length = len; } } done: if (status != MLAN_STATUS_PENDING) kfree(req); LEAVE(); return ret; } /** * @brief Get esupp mode * * @param priv A pointer to moal_private structure * @param wrq A pointer to iwreq structure * * @return 0 --success, otherwise fail */ static int woal_get_esupp_mode(moal_private *priv, struct iwreq *wrq) { int ret = 0; mlan_ds_sec_cfg *sec = NULL; mlan_ioctl_req *req = NULL; mlan_status status = MLAN_STATUS_SUCCESS; ENTER(); if (!priv->phandle->card_info->embedded_supp) { PRINTM(MERROR, "Not supported cmd on this card\n"); ret = -EOPNOTSUPP; goto done; } req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg)); if (req == NULL) { ret = -ENOMEM; goto done; } sec = (mlan_ds_sec_cfg *)req->pbuf; sec->sub_command = MLAN_OID_SEC_CFG_ESUPP_MODE; req->req_id = MLAN_IOCTL_SEC_CFG; req->action = MLAN_ACT_GET; status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto done; } if (copy_to_user(wrq->u.data.pointer, (t_u8 *)&sec->param.esupp_mode, sizeof(int) * 3)) { ret = -EFAULT; goto done; } wrq->u.data.length = 3; done: if (status != MLAN_STATUS_PENDING) kfree(req); LEAVE(); return ret; } /** * @brief Get GTK/PTK * * @param priv A pointer to moal_private structure * @param wrq A pointer to user data * * @return 0 --success, otherwise fail */ static int woal_get_key_ioctl(moal_private *priv, struct iwreq *wrq) { int ret = 0; unsigned int i; t_u8 key_ascii[256]; t_u8 *tmp; mlan_ds_sec_cfg *sec = NULL; mlan_ioctl_req *req = NULL; mlan_status status = MLAN_STATUS_SUCCESS; ENTER(); memset(key_ascii, 0x00, sizeof(key_ascii)); tmp = key_ascii; if (priv->media_connected == MFALSE) { PRINTM(MERROR, "Can't get key in un-associated state\n"); ret = -EFAULT; goto done; } req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg)); if (req == NULL) { ret = -ENOMEM; goto done; } /* Get Unicast Key */ req->req_id = MLAN_IOCTL_SEC_CFG; req->action = MLAN_ACT_GET; sec = (mlan_ds_sec_cfg *)req->pbuf; sec->sub_command = MLAN_OID_SEC_QUERY_KEY; sec->param.encrypt_key.key_index = 0; sec->param.encrypt_key.key_flags = 0; status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto done; } if (sec->param.encrypt_key.key_len) { sprintf((char *)tmp, "\n%s", "PTK: "); tmp += 5; for (i = 0; i < sec->param.encrypt_key.key_len; i++) tmp += sprintf((char *)tmp, "%02x", sec->param.encrypt_key.key_material[i]); } /* Get Multicase Key */ req->req_id = MLAN_IOCTL_SEC_CFG; req->action = MLAN_ACT_GET; sec = (mlan_ds_sec_cfg *)req->pbuf; sec->sub_command = MLAN_OID_SEC_QUERY_KEY; sec->param.encrypt_key.key_index = 0; sec->param.encrypt_key.key_flags = KEY_FLAG_GROUP_KEY; memset(sec->param.encrypt_key.mac_addr, 0x0, MLAN_MAC_ADDR_LENGTH); status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto done; } if (sec->param.encrypt_key.key_len) { sprintf((char *)tmp, "\n%s", "GTK: "); tmp += 5; for (i = 0; i < sec->param.encrypt_key.key_len; i++) tmp += sprintf((char *)tmp, "%02x", sec->param.encrypt_key.key_material[i]); } /* Get IGTK Key */ req->req_id = MLAN_IOCTL_SEC_CFG; req->action = MLAN_ACT_GET; sec = (mlan_ds_sec_cfg *)req->pbuf; sec->sub_command = MLAN_OID_SEC_QUERY_KEY; sec->param.encrypt_key.key_index = 0; sec->param.encrypt_key.key_flags = KEY_FLAG_AES_MCAST_IGTK; memset(sec->param.encrypt_key.mac_addr, 0x0, MLAN_MAC_ADDR_LENGTH); status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto done; } if (sec->param.encrypt_key.key_len) { sprintf((char *)tmp, "\n%s", "IGTK: "); tmp += 6; for (i = 0; i < sec->param.encrypt_key.key_len; i++) tmp += sprintf((char *)tmp, "%02x", sec->param.encrypt_key.key_material[i]); } wrq->u.data.length = sizeof(key_ascii) + 1; if (wrq->u.data.pointer) { if (copy_to_user(wrq->u.data.pointer, &key_ascii, tmp - key_ascii)) { PRINTM(MERROR, "copy_to_user failed\n"); ret = -EFAULT; goto done; } } done: if (status != MLAN_STATUS_PENDING) kfree(req); LEAVE(); return ret; } /** * @brief arpfilter ioctl function * * @param priv A pointer to moal_private structure * @param wrq A pointer to iwreq structure * @return 0 --success, otherwise fail */ static int woal_arp_filter(moal_private *priv, struct iwreq *wrq) { int ret = 0; mlan_ds_misc_cfg *misc = NULL; mlan_ioctl_req *req = NULL; int data_length = wrq->u.data.length, copy_len; mlan_status status = MLAN_STATUS_SUCCESS; ENTER(); copy_len = MIN(sizeof(misc->param.gen_ie.ie_data), sizeof(int) * data_length); req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); if (req == NULL) { ret = -ENOMEM; goto done; } misc = (mlan_ds_misc_cfg *)req->pbuf; misc->sub_command = MLAN_OID_MISC_GEN_IE; req->req_id = MLAN_IOCTL_MISC_CFG; req->action = MLAN_ACT_SET; misc->param.gen_ie.type = MLAN_IE_TYPE_ARP_FILTER; misc->param.gen_ie.len = data_length; /* get the whole command from user */ if (copy_from_user(misc->param.gen_ie.ie_data, wrq->u.data.pointer, copy_len)) { PRINTM(MERROR, "copy from user failed\n"); ret = -EFAULT; goto done; } status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto done; } done: if (status != MLAN_STATUS_PENDING) kfree(req); LEAVE(); return ret; } /** * @brief Set/get IP address * * @param priv A pointer to moal_private structure * @param wrq A pointer to iwreq structure * @return 0 --success, otherwise fail */ static int woal_set_get_ip_addr(moal_private *priv, struct iwreq *wrq) { char buf[IPADDR_MAX_BUF]; mlan_ioctl_req *ioctl_req = NULL; mlan_ds_misc_cfg *misc = NULL; int ret = 0, op_code = 0, data_length = wrq->u.data.length; mlan_status status = MLAN_STATUS_SUCCESS; ENTER(); memset(buf, 0, IPADDR_MAX_BUF); ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); if (ioctl_req == NULL) { ret = -ENOMEM; goto done; } misc = (mlan_ds_misc_cfg *)ioctl_req->pbuf; if (data_length <= 1) { /* GET */ ioctl_req->action = MLAN_ACT_GET; } else { if (copy_from_user(buf, wrq->u.data.pointer, MIN(IPADDR_MAX_BUF - 1, data_length))) { PRINTM(MERROR, "Copy from user failed\n"); ret = -EFAULT; goto done; } /* Make sure we have the operation argument */ if (data_length > 2 && buf[1] != ';') { PRINTM(MERROR, "No operation argument. Separate with ';'\n"); ret = -EINVAL; goto done; } else { buf[1] = '\0'; } ioctl_req->action = MLAN_ACT_SET; /* only one IP is supported in current firmware */ memset(misc->param.ipaddr_cfg.ip_addr[0], 0, IPADDR_LEN); in4_pton(&buf[2], MIN((IPADDR_MAX_BUF - 3), (data_length - 2)), misc->param.ipaddr_cfg.ip_addr[0], ' ', NULL); /* only one IP is supported in current firmware */ misc->param.ipaddr_cfg.ip_addr_num = 1; misc->param.ipaddr_cfg.ip_addr_type = IPADDR_TYPE_IPV4; } if (woal_atoi(&op_code, buf) != MLAN_STATUS_SUCCESS) { ret = -EINVAL; goto done; } misc->param.ipaddr_cfg.op_code = (t_u32)op_code; ioctl_req->req_id = MLAN_IOCTL_MISC_CFG; misc->sub_command = MLAN_OID_MISC_IP_ADDR; /* Send ioctl to mlan */ status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto done; } if (ioctl_req->action == MLAN_ACT_GET) { snprintf(buf, IPADDR_MAX_BUF, "%d;%d.%d.%d.%d", misc->param.ipaddr_cfg.op_code, misc->param.ipaddr_cfg.ip_addr[0][0], misc->param.ipaddr_cfg.ip_addr[0][1], misc->param.ipaddr_cfg.ip_addr[0][2], misc->param.ipaddr_cfg.ip_addr[0][3]); wrq->u.data.length = IPADDR_MAX_BUF; if (copy_to_user(wrq->u.data.pointer, buf, IPADDR_MAX_BUF)) { PRINTM(MERROR, "Copy to user failed\n"); ret = -EFAULT; } } done: if (status != MLAN_STATUS_PENDING) kfree(ioctl_req); LEAVE(); return ret; } /** * @brief Set/Get Transmit beamforming capabilities * * @param priv A pointer to moal_private structure * @param wrq A pointer to iwreq structure * * @return 0 -- success, otherwise fail */ static int woal_tx_bf_cap_ioctl(moal_private *priv, struct iwreq *wrq) { int ret = 0, data_length = wrq->u.data.length; mlan_ioctl_req *req = NULL; mlan_ds_11n_cfg *bf_cfg = NULL; int bf_cap = 0; mlan_status status = MLAN_STATUS_SUCCESS; ENTER(); if (data_length > 1) { PRINTM(MERROR, "Invalid no of arguments!\n"); ret = -EINVAL; goto done; } /* Allocate an IOCTL request buffer */ req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); if (req == NULL) { ret = -ENOMEM; goto done; } /* Fill request buffer */ bf_cfg = (mlan_ds_11n_cfg *)req->pbuf; req->req_id = MLAN_IOCTL_11N_CFG; bf_cfg->sub_command = MLAN_OID_11N_CFG_TX_BF_CAP; req->action = MLAN_ACT_GET; if (data_length) { /* SET */ if (copy_from_user(&bf_cap, wrq->u.data.pointer, sizeof(int))) { PRINTM(MERROR, "copy from user failed\n"); ret = -EFAULT; goto done; } bf_cfg->param.tx_bf_cap = bf_cap; req->action = MLAN_ACT_SET; } /* Send IOCTL request to MLAN */ status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto done; } bf_cap = bf_cfg->param.tx_bf_cap; if (copy_to_user(wrq->u.data.pointer, &bf_cap, sizeof(int))) { ret = -EFAULT; goto done; } wrq->u.data.length = 1; done: if (status != MLAN_STATUS_PENDING) kfree(req); LEAVE(); return ret; } /* Maximum input output characters in group WOAL_SET_GET_256_CHAR */ #define MAX_IN_OUT_CHAR 256 /** Tx BF Global conf argument index */ #define BF_ENABLE_PARAM 1 #define SOUND_ENABLE_PARAM 2 #define FB_TYPE_PARAM 3 #define SNR_THRESHOLD_PARAM 4 #define SOUND_INTVL_PARAM 5 #define BF_MODE_PARAM 6 #define BF_CFG_ACT_GET 0 #define BF_CFG_ACT_SET 1 /** * @brief Set/Get Transmit beamforming configuration * * @param priv A pointer to moal_private structure * @param wrq A pointer to iwreq structure * * @return 0 -- success, otherwise fail */ static int woal_tx_bf_cfg_ioctl(moal_private *priv, struct iwreq *wrq) { int ret = 0, data_length = wrq->u.data.length; int bf_action = 0, interval = 0; int snr = 0, i, tmp_val = 0; t_u8 buf[MAX_IN_OUT_CHAR], char_count = 0; t_u8 *str, *token, *pos; t_u16 action = 0; mlan_ds_11n_tx_bf_cfg bf_cfg; mlan_trigger_sound_args *bf_sound = NULL; mlan_tx_bf_peer_args *tx_bf_peer = NULL; mlan_snr_thr_args *bf_snr = NULL; mlan_bf_periodicity_args *bf_periodicity = NULL; mlan_bf_global_cfg_args *bf_global = NULL; ENTER(); memset(&bf_cfg, 0, sizeof(bf_cfg)); /* Pointer to corresponding buffer */ bf_sound = bf_cfg.body.bf_sound; tx_bf_peer = bf_cfg.body.tx_bf_peer; bf_snr = bf_cfg.body.bf_snr; bf_periodicity = bf_cfg.body.bf_periodicity; bf_global = &bf_cfg.body.bf_global_cfg; /* Total characters in buffer */ char_count = data_length - 1; memset(buf, 0, sizeof(buf)); if (char_count) { if (data_length > (int)sizeof(buf)) { PRINTM(MERROR, "Too many arguments\n"); ret = -EINVAL; goto done; } if (copy_from_user(buf, wrq->u.data.pointer, data_length)) { PRINTM(MERROR, "copy from user failed\n"); ret = -EFAULT; goto done; } if (char_count > 1 && buf[1] != ';') { PRINTM(MERROR, "No action argument. Separate with ';'\n"); ret = -EINVAL; goto done; } /* Replace ';' with NULL in the string to separate args */ for (i = 0; i < char_count; i++) { if (buf[i] == ';') buf[i] = '\0'; } /* The first byte represents the beamforming action */ if (woal_atoi(&bf_action, &buf[0]) != MLAN_STATUS_SUCCESS) { ret = -EINVAL; goto done; } switch (bf_action) { case BF_GLOBAL_CONFIGURATION: if (char_count == 1) { action = MLAN_ACT_GET; bf_cfg.action = BF_CFG_ACT_GET; } else { action = MLAN_ACT_SET; bf_cfg.action = BF_CFG_ACT_SET; /* Eliminate action field */ token = &buf[2]; for (i = 1, str = &buf[2]; token != NULL; i++) { token = strstr(str, " "); pos = str; if (token != NULL) { *token = '\0'; str = token + 1; } woal_atoi(&tmp_val, pos); switch (i) { case BF_ENABLE_PARAM: bf_global->bf_enbl = (t_u8)tmp_val; break; case SOUND_ENABLE_PARAM: bf_global->sounding_enbl = (t_u8)tmp_val; break; case FB_TYPE_PARAM: bf_global->fb_type = (t_u8)tmp_val; break; case SNR_THRESHOLD_PARAM: bf_global->snr_threshold = (t_u8)tmp_val; break; case SOUND_INTVL_PARAM: bf_global->sounding_interval = (t_u16)tmp_val; break; case BF_MODE_PARAM: bf_global->bf_mode = (t_u8)tmp_val; break; default: PRINTM(MERROR, "Invalid Argument\n"); ret = -EINVAL; goto done; } } } break; case TRIGGER_SOUNDING_FOR_PEER: /* * First arg = 2 BfAction * Second arg = 17 MAC "00:50:43:20:BF:64" */ if (char_count != 19) { PRINTM(MERROR, "Invalid argument\n"); ret = -EINVAL; goto done; } woal_mac2u8(bf_sound->peer_mac, &buf[2]); action = MLAN_ACT_SET; bf_cfg.action = BF_CFG_ACT_SET; break; case SET_GET_BF_PERIODICITY: /* * First arg = 2 BfAction * Second arg = 18 MAC "00:50:43:20:BF:64;" * Third arg = 1 (min char) TX BF interval * 10 (max char) u32 maximum value * 4294967295 */ if (char_count < 19 || char_count > 30) { PRINTM(MERROR, "Invalid argument\n"); ret = -EINVAL; goto done; } woal_mac2u8(bf_periodicity->peer_mac, &buf[2]); if (char_count == 19) { action = MLAN_ACT_GET; bf_cfg.action = BF_CFG_ACT_GET; } else { action = MLAN_ACT_SET; bf_cfg.action = BF_CFG_ACT_SET; if (woal_atoi(&interval, &buf[20]) != MLAN_STATUS_SUCCESS) { ret = -EINVAL; goto done; } bf_periodicity->interval = interval; } break; case TX_BF_FOR_PEER_ENBL: /* * Handle only SET operation here * First arg = 2 BfAction * Second arg = 18 MAC "00:50:43:20:BF:64;" * Third arg = 2 enable/disable bf * Fourth arg = 2 enable/disable sounding * Fifth arg = 1 FB Type */ if (char_count != 25 && char_count != 1) { PRINTM(MERROR, "Invalid argument\n"); ret = -EINVAL; goto done; } if (char_count == 1) { action = MLAN_ACT_GET; bf_cfg.action = BF_CFG_ACT_GET; } else { woal_mac2u8(tx_bf_peer->peer_mac, &buf[2]); woal_atoi(&tmp_val, &buf[20]); tx_bf_peer->bf_enbl = (t_u8)tmp_val; woal_atoi(&tmp_val, &buf[22]); tx_bf_peer->sounding_enbl = (t_u8)tmp_val; woal_atoi(&tmp_val, &buf[24]); tx_bf_peer->fb_type = (t_u8)tmp_val; action = MLAN_ACT_SET; bf_cfg.action = BF_CFG_ACT_SET; } break; case SET_SNR_THR_PEER: /* * First arg = 2 BfAction * Second arg = 18 MAC "00:50:43:20:BF:64;" * Third arg = 1/2 SNR u8 - can be 1/2 charerters */ if (char_count != 1 && !(char_count == 21 || char_count == 22)) { PRINTM(MERROR, "Invalid argument\n"); ret = -EINVAL; goto done; } if (char_count == 1) { action = MLAN_ACT_GET; bf_cfg.action = BF_CFG_ACT_GET; } else { woal_mac2u8(bf_snr->peer_mac, &buf[2]); if (woal_atoi(&snr, &buf[20]) != MLAN_STATUS_SUCCESS) { ret = -EINVAL; goto done; } bf_snr->snr = snr; action = MLAN_ACT_SET; bf_cfg.action = BF_CFG_ACT_SET; } break; default: ret = -EINVAL; goto done; } /* Save the value */ bf_cfg.bf_action = bf_action; if (MLAN_STATUS_SUCCESS != woal_set_get_tx_bf_cfg(priv, action, &bf_cfg)) { ret = -EFAULT; goto done; } } else { ret = -EINVAL; goto done; } if (action == MLAN_ACT_GET) { data_length = 0; memset(buf, 0, sizeof(buf)); switch (bf_action) { case BF_GLOBAL_CONFIGURATION: data_length += sprintf(buf + data_length, "%d ", (int)bf_global->bf_enbl); data_length += sprintf(buf + data_length, "%d ", (int)bf_global->sounding_enbl); data_length += sprintf(buf + data_length, "%d ", (int)bf_global->fb_type); data_length += sprintf(buf + data_length, "%d ", (int)bf_global->snr_threshold); data_length += sprintf(buf + data_length, "%d ", (int)bf_global->sounding_interval); data_length += sprintf(buf + data_length, "%d ", (int)bf_global->bf_mode); break; case SET_GET_BF_PERIODICITY: data_length += sprintf(buf + data_length, "%02x:%02x:%02x:%02x:%02x:%02x", bf_periodicity->peer_mac[0], bf_periodicity->peer_mac[1], bf_periodicity->peer_mac[2], bf_periodicity->peer_mac[3], bf_periodicity->peer_mac[4], bf_periodicity->peer_mac[5]); data_length += sprintf(buf + data_length, "%c", ' '); data_length += sprintf(buf + data_length, "%d", bf_periodicity->interval); break; case TX_BF_FOR_PEER_ENBL: for (i = 0; i < (int)bf_cfg.no_of_peers; i++) { data_length += sprintf(buf + data_length, "%02x:%02x:%02x:%02x:%02x:%02x", tx_bf_peer->peer_mac[0], tx_bf_peer->peer_mac[1], tx_bf_peer->peer_mac[2], tx_bf_peer->peer_mac[3], tx_bf_peer->peer_mac[4], tx_bf_peer->peer_mac[5]); data_length += sprintf(buf + data_length, "%c", ' '); data_length += sprintf(buf + data_length, "%d;", tx_bf_peer->bf_enbl); data_length += sprintf(buf + data_length, "%d;", tx_bf_peer->sounding_enbl); data_length += sprintf(buf + data_length, "%d ", tx_bf_peer->fb_type); tx_bf_peer++; } break; case SET_SNR_THR_PEER: for (i = 0; i < (int)bf_cfg.no_of_peers; i++) { data_length += sprintf(buf + data_length, "%02x:%02x:%02x:%02x:%02x:%02x", bf_snr->peer_mac[0], bf_snr->peer_mac[1], bf_snr->peer_mac[2], bf_snr->peer_mac[3], bf_snr->peer_mac[4], bf_snr->peer_mac[5]); data_length += sprintf(buf + data_length, "%c", ';'); data_length += sprintf(buf + data_length, "%d", bf_snr->snr); data_length += sprintf(buf + data_length, "%c", ' '); bf_snr++; } break; } buf[data_length] = '\0'; } wrq->u.data.length = data_length; if (copy_to_user(wrq->u.data.pointer, buf, wrq->u.data.length)) { ret = -EFAULT; goto done; } done: LEAVE(); return ret; } /** * @brief Retrieve the scan response/beacon table * * @param wrq A pointer to iwreq structure * @param scan_resp A pointer to mlan_scan_resp structure * @param scan_start argument * * @return MLAN_STATUS_SUCCESS --success, otherwise fail */ static int moal_ret_get_scan_table_ioctl(struct iwreq *wrq, mlan_scan_resp *scan_resp, t_u32 scan_start) { pBSSDescriptor_t pbss_desc, scan_table; wlan_ioctl_get_scan_table_info *prsp_info; int ret_code; int ret_len; int space_left; t_u8 *pcurrent; t_u8 *pbuffer_end; t_u32 num_scans_done; ENTER(); num_scans_done = 0; ret_code = MLAN_STATUS_SUCCESS; prsp_info = (wlan_ioctl_get_scan_table_info __force *)wrq->u.data.pointer; pcurrent = (t_u8 *)prsp_info->scan_table_entry_buf; pbuffer_end = (t_u8 __force *)wrq->u.data.pointer + wrq->u.data.length - 1; space_left = pbuffer_end - pcurrent; scan_table = (BSSDescriptor_t *)(scan_resp->pscan_table); PRINTM(MINFO, "GetScanTable: scan_start req = %d\n", scan_start); PRINTM(MINFO, "GetScanTable: length avail = %d\n", wrq->u.data.length); if (!scan_start) { PRINTM(MINFO, "GetScanTable: get current BSS Descriptor\n"); /* Use to get current association saved descriptor */ pbss_desc = scan_table; ret_code = wlan_get_scan_table_ret_entry(pbss_desc, &pcurrent, &space_left); if (ret_code == MLAN_STATUS_SUCCESS) num_scans_done = 1; } else { scan_start--; while (space_left && (scan_start + num_scans_done < scan_resp->num_in_scan_table) && (ret_code == MLAN_STATUS_SUCCESS)) { pbss_desc = (scan_table + (scan_start + num_scans_done)); PRINTM(MINFO, "GetScanTable: get current BSS Descriptor [%d]\n", scan_start + num_scans_done); ret_code = wlan_get_scan_table_ret_entry( pbss_desc, &pcurrent, &space_left); if (ret_code == MLAN_STATUS_SUCCESS) num_scans_done++; } } prsp_info->scan_number = num_scans_done; ret_len = pcurrent - (t_u8 __force *)wrq->u.data.pointer; wrq->u.data.length = ret_len; /* Return ret_code (EFAULT or E2BIG) in the case where no scan results * were successfully encoded. */ LEAVE(); return num_scans_done ? MLAN_STATUS_SUCCESS : ret_code; } /** * @brief Get scan table ioctl * * @param priv A pointer to moal_private structure * @param wrq A pointer to iwreq structure * * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, * otherwise fail */ static mlan_status woal_get_scan_table_ioctl(moal_private *priv, struct iwreq *wrq) { mlan_ioctl_req *req = NULL; mlan_ds_scan *scan = NULL; int scan_start = 0; mlan_status status = MLAN_STATUS_SUCCESS; ENTER(); /* Allocate an IOCTL request buffer */ req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_scan)); if (req == NULL) { status = MLAN_STATUS_FAILURE; goto done; } /* Fill request buffer */ scan = (mlan_ds_scan *)req->pbuf; req->req_id = MLAN_IOCTL_SCAN; req->action = MLAN_ACT_GET; /* get the whole command from user */ if (copy_from_user(&scan_start, wrq->u.data.pointer, sizeof(scan_start))) { PRINTM(MERROR, "copy from user failed\n"); goto done; } if (scan_start > 0) scan->sub_command = MLAN_OID_SCAN_NORMAL; else scan->sub_command = MLAN_OID_SCAN_GET_CURRENT_BSS; /* Send IOCTL request to MLAN */ status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); if (status == MLAN_STATUS_SUCCESS) { status = moal_ret_get_scan_table_ioctl( wrq, &scan->param.scan_resp, scan_start); } done: if (status != MLAN_STATUS_PENDING) kfree(req); LEAVE(); return status; } /** * @brief Set user scan ext -- Async mode, without wait * * @param priv A pointer to moal_private structure * @param wrq A pointer to iwreq structure * * @return 0 -- success, otherwise fail */ static int woal_set_user_scan_ext_ioctl(moal_private *priv, struct iwreq *wrq) { int ret = 0; wlan_user_scan_cfg *scan_req; ENTER(); scan_req = (wlan_user_scan_cfg *)kmalloc(sizeof(wlan_user_scan_cfg), GFP_KERNEL); if (!scan_req) { PRINTM(MERROR, "Malloc buffer failed\n"); LEAVE(); return -ENOMEM; } memset(scan_req, 0x00, sizeof(wlan_user_scan_cfg)); if (copy_from_user(scan_req, wrq->u.data.pointer, MIN(wrq->u.data.length, sizeof(wlan_user_scan_cfg)))) { PRINTM(MINFO, "Copy from user failed\n"); LEAVE(); return -EFAULT; } if (MLAN_STATUS_FAILURE == woal_do_scan(priv, scan_req)) ret = -EFAULT; kfree(scan_req); LEAVE(); return ret; } /** * @brief Set user scan * * @param priv A pointer to moal_private structure * @param wrq A pointer to iwreq structure * * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, * otherwise fail */ static mlan_status woal_set_user_scan_ioctl(moal_private *priv, struct iwreq *wrq) { int ret = 0; mlan_ioctl_req *req = NULL; mlan_ds_scan *scan = NULL; mlan_status status = MLAN_STATUS_SUCCESS; union iwreq_data wrqu; moal_handle *handle = priv->phandle; ENTER(); if (handle->scan_pending_on_block == MTRUE) { PRINTM(MINFO, "scan already in processing...\n"); LEAVE(); return ret; } if (MOAL_ACQ_SEMAPHORE_BLOCK(&handle->async_sem)) { PRINTM(MERROR, "Acquire semaphore error, request_scan\n"); LEAVE(); return MLAN_STATUS_FAILURE; } handle->scan_pending_on_block = MTRUE; handle->scan_priv = priv; /* Allocate an IOCTL request buffer */ req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_scan) + wrq->u.data.length); if (req == NULL) { ret = -ENOMEM; goto done; } /* Fill request buffer */ scan = (mlan_ds_scan *)req->pbuf; scan->sub_command = MLAN_OID_SCAN_USER_CONFIG; req->req_id = MLAN_IOCTL_SCAN; req->action = MLAN_ACT_SET; if (copy_from_user(scan->param.user_scan.scan_cfg_buf, wrq->u.data.pointer, wrq->u.data.length)) { PRINTM(MINFO, "Copy from user failed\n"); ret = -EFAULT; goto done; } /* Send IOCTL request to MLAN */ status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); if (status == MLAN_STATUS_SUCCESS) { memset(&wrqu, 0, sizeof(union iwreq_data)); wireless_send_event(priv->netdev, SIOCGIWSCAN, &wrqu, NULL); } done: handle->scan_pending_on_block = MFALSE; handle->scan_priv = NULL; MOAL_REL_SEMAPHORE(&handle->async_sem); if (status != MLAN_STATUS_PENDING) kfree(req); LEAVE(); return status; } #ifdef SDIO /** * @brief Cmd52 read/write register * * @param priv A pointer to moal_private structure * @param wrq A pointer to iwreq structure * @return MLAN_STATUS_SUCCESS --success, otherwise fail */ static int woal_cmd52rdwr_ioctl(moal_private *priv, struct iwreq *wrq) { t_u8 rw = 0, func, data = 0; int buf[3], reg, ret = MLAN_STATUS_SUCCESS; int data_length = wrq->u.data.length; ENTER(); if (data_length < 2 || data_length > 3) { PRINTM(MERROR, "Invalid number of arguments\n"); ret = -EINVAL; goto done; } if (copy_from_user(buf, wrq->u.data.pointer, sizeof(int) * data_length)) { PRINTM(MERROR, "Copy from user failed\n"); ret = -EFAULT; goto done; } func = (t_u8)buf[0]; if (func > 7) { PRINTM(MERROR, "Invalid function number!\n"); ret = -EINVAL; goto done; } reg = (t_u32)buf[1]; if (data_length == 2) { rw = 0; /* CMD52 read */ PRINTM(MINFO, "Cmd52 read, func=%d, reg=0x%08X\n", func, reg); } if (data_length == 3) { rw = 1; /* CMD52 write */ data = (t_u8)buf[2]; PRINTM(MINFO, "Cmd52 write, func=%d, reg=0x%08X, data=0x%02X\n", func, reg, data); } if (!rw) { #ifdef SDIO_MMC sdio_claim_host( ((struct sdio_mmc_card *)priv->phandle->card)->func); if (func) data = sdio_readb( ((struct sdio_mmc_card *)priv->phandle->card) ->func, reg, &ret); else data = sdio_f0_readb( ((struct sdio_mmc_card *)priv->phandle->card) ->func, reg, &ret); sdio_release_host( ((struct sdio_mmc_card *)priv->phandle->card)->func); if (ret) { PRINTM(MERROR, "sdio_readb: reading register 0x%X failed\n", reg); goto done; } #else if (sdio_read_ioreg(priv->phandle->card, func, reg, &data) < 0) { PRINTM(MERROR, "sdio_read_ioreg: reading register 0x%X failed\n", reg); ret = MLAN_STATUS_FAILURE; goto done; } #endif } else { #ifdef SDIO_MMC sdio_claim_host( ((struct sdio_mmc_card *)priv->phandle->card)->func); if (func) sdio_writeb( ((struct sdio_mmc_card *)priv->phandle->card) ->func, data, reg, &ret); else sdio_f0_writeb( ((struct sdio_mmc_card *)priv->phandle->card) ->func, data, reg, &ret); sdio_release_host( ((struct sdio_mmc_card *)priv->phandle->card)->func); if (ret) { PRINTM(MERROR, "sdio_writeb: writing register 0x%X failed\n", reg); goto done; } #else if (sdio_write_ioreg(priv->phandle->card, func, reg, data) < 0) { PRINTM(MERROR, "sdio_write_ioreg: writing register 0x%X failed\n", reg); ret = MLAN_STATUS_FAILURE; goto done; } #endif } buf[0] = data; wrq->u.data.length = 1; if (copy_to_user(wrq->u.data.pointer, buf, sizeof(int))) { PRINTM(MERROR, "Copy to user failed\n"); ret = -EFAULT; goto done; } done: LEAVE(); return ret; } /** * @brief Cmd53 read/write register * * @param priv A pointer to moal_private structure * @param wrq A pointer to iwreq structure * @return MLAN_STATUS_SUCCESS --success, otherwise fail */ static int woal_cmd53rdwr_ioctl(moal_private *priv, struct iwreq *wrq) { t_u8 *buf = NULL; t_u8 rw, mode; t_u16 blklen = 0, blknum = 0; int reg = 0, pattern_len = 0, pos = 0, ret = MLAN_STATUS_SUCCESS; t_u32 total_len = 0; t_u8 *data = NULL; ENTER(); buf = kmalloc(WOAL_2K_BYTES, GFP_KERNEL); if (!buf) { PRINTM(MERROR, "Cannot allocate buffer for command!\n"); ret = -EFAULT; goto done; } data = kmalloc(WOAL_2K_BYTES, GFP_KERNEL); if (!data) { PRINTM(MERROR, "Cannot allocate buffer for command!\n"); ret = -EFAULT; goto done; } if (wrq->u.data.length > WOAL_2K_BYTES) { PRINTM(MERROR, "Data lengh is too large!\n"); ret = -EINVAL; goto done; } if (copy_from_user(buf, wrq->u.data.pointer, wrq->u.data.length)) { PRINTM(MINFO, "Copy from user failed\n"); ret = -EFAULT; goto done; } rw = buf[0]; /* read/write (0/1) */ reg = buf[5]; /* address */ reg = (reg << 8) + buf[4]; reg = (reg << 8) + buf[3]; reg = (reg << 8) + buf[2]; mode = buf[6]; /* byte mode/block mode (0/1) */ blklen = buf[8]; /* block size */ blklen = (blklen << 8) + buf[7]; blknum = buf[10]; /* block number or byte number */ blknum = (blknum << 8) + buf[9]; if (mode != BYTE_MODE) mode = BLOCK_MODE; total_len = (mode == BLOCK_MODE) ? blknum * blklen : blknum; if (total_len > WOAL_2K_BYTES) { PRINTM(MERROR, "Total data length is too large!\n"); ret = -EINVAL; goto done; } PRINTM(MINFO, "CMD53 read/write, addr = %#x, mode = %d, block size = %d, block(byte) number = %d\n", reg, mode, blklen, blknum); if (!rw) { sdio_claim_host( ((struct sdio_mmc_card *)priv->phandle->card)->func); if (sdio_readsb( ((struct sdio_mmc_card *)priv->phandle->card)->func, data, reg, total_len)) PRINTM(MERROR, "sdio_readsb: reading memory 0x%x failed\n", reg); sdio_release_host( ((struct sdio_mmc_card *)priv->phandle->card)->func); if (copy_to_user(wrq->u.data.pointer, data, total_len)) { PRINTM(MINFO, "Copy to user failed\n"); ret = -EFAULT; goto done; } wrq->u.data.length = total_len; } else { pattern_len = wrq->u.data.length - 11; if (pattern_len > (int)total_len) pattern_len = total_len; memset(data, 0, WOAL_2K_BYTES); /* Copy/duplicate the pattern to data buffer */ for (pos = 0; pos < (int)total_len; pos++) data[pos] = buf[11 + (pos % pattern_len)]; sdio_claim_host( ((struct sdio_mmc_card *)priv->phandle->card)->func); if (sdio_writesb( ((struct sdio_mmc_card *)priv->phandle->card)->func, reg, data, total_len)) PRINTM(MERROR, "sdio_writesb: writing memory 0x%x failed\n", reg); sdio_release_host( ((struct sdio_mmc_card *)priv->phandle->card)->func); } done: kfree(buf); kfree(data); LEAVE(); return ret; } #endif /* SDIO */ #ifdef SDIO /** * @brief Set SDIO Multi-point aggregation control parameters * * @param priv A pointer to moal_private structure * @param wrq A pointer to iwreq structure * * @return 0/MLAN_STATUS_PENDING --success, otherwise fail */ static int woal_do_sdio_mpa_ctrl(moal_private *priv, struct iwreq *wrq) { int data[6], data_length = wrq->u.data.length, copy_len; int ret = 0; mlan_ds_misc_cfg *misc = NULL; mlan_ioctl_req *req = NULL; mlan_status status = MLAN_STATUS_SUCCESS; ENTER(); if (sizeof(int) * wrq->u.data.length > sizeof(data)) { PRINTM(MERROR, "Too many arguments\n"); ret = -EINVAL; goto done; } copy_len = MIN(sizeof(data), sizeof(int) * data_length); req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); if (req == NULL) { ret = -ENOMEM; goto done; } misc = (mlan_ds_misc_cfg *)req->pbuf; memset(misc, 0, sizeof(mlan_ds_misc_cfg)); misc->sub_command = MLAN_OID_MISC_SDIO_MPA_CTRL; req->req_id = MLAN_IOCTL_MISC_CFG; /* Get the values first, then modify these values if * user had modified them */ req->action = MLAN_ACT_GET; status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { PRINTM(MERROR, "woal_request_ioctl returned %d\n", ret); ret = -EFAULT; goto done; } if (data_length == 0) { data[0] = misc->param.mpa_ctrl.tx_enable; data[1] = misc->param.mpa_ctrl.rx_enable; data[2] = misc->param.mpa_ctrl.tx_buf_size; data[3] = misc->param.mpa_ctrl.rx_buf_size; data[4] = misc->param.mpa_ctrl.tx_max_ports; data[5] = misc->param.mpa_ctrl.rx_max_ports; PRINTM(MINFO, "Get Param: %d %d %d %d %d %d\n", data[0], data[1], data[2], data[3], data[4], data[5]); if (copy_to_user(wrq->u.data.pointer, data, sizeof(data))) { ret = -EFAULT; goto done; } wrq->u.data.length = ARRAY_SIZE(data); goto done; } if (copy_from_user(data, wrq->u.data.pointer, copy_len)) { PRINTM(MINFO, "Copy from user failed\n"); ret = -EFAULT; goto done; } switch (data_length) { case 6: misc->param.mpa_ctrl.rx_max_ports = data[5]; /* fall through */ case 5: misc->param.mpa_ctrl.tx_max_ports = data[4]; /* fall through */ case 4: misc->param.mpa_ctrl.rx_buf_size = data[3]; /* fall through */ case 3: misc->param.mpa_ctrl.tx_buf_size = data[2]; /* fall through */ case 2: misc->param.mpa_ctrl.rx_enable = data[1]; /* fall through */ case 1: /* Set cmd */ req->action = MLAN_ACT_SET; PRINTM(MINFO, "Set Param: %d %d %d %d %d %d\n", data[0], data[1], data[2], data[3], data[4], data[5]); misc->param.mpa_ctrl.tx_enable = data[0]; break; default: PRINTM(MERROR, "Default case error\n"); ret = -EINVAL; goto done; } status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { PRINTM(MERROR, "woal_request_ioctl returned %d\n", ret); ret = -EFAULT; goto done; } done: if (status != MLAN_STATUS_PENDING) kfree(req); LEAVE(); return ret; } #endif /** * @brief Set/Get scan configuration parameters * * @param priv A pointer to moal_private structure * @param wrq A pointer to iwreq structure * * @return 0 --success, otherwise fail */ static int woal_set_get_scan_cfg(moal_private *priv, struct iwreq *wrq) { int ret = 0; int data[7], copy_len; mlan_ds_scan *scan = NULL; mlan_ioctl_req *req = NULL; int data_length = wrq->u.data.length; mlan_status status = MLAN_STATUS_SUCCESS; int arg_len = 0; ENTER(); arg_len = 7; if (data_length > arg_len) { PRINTM(MERROR, "Too much arguments\n"); LEAVE(); return -EINVAL; } copy_len = sizeof(int) * data_length; req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_scan)); if (req == NULL) { LEAVE(); return -ENOMEM; } scan = (mlan_ds_scan *)req->pbuf; scan->sub_command = MLAN_OID_SCAN_CONFIG; req->req_id = MLAN_IOCTL_SCAN; memset(data, 0, sizeof(data)); if (data_length) { if (copy_from_user(data, wrq->u.data.pointer, copy_len)) { PRINTM(MERROR, "Copy from user failed\n"); ret = -EFAULT; goto done; } if ((data[0] < 0) || (data[0] > MLAN_SCAN_TYPE_PASSIVE)) { PRINTM(MERROR, "Invalid argument for scan type\n"); ret = -EINVAL; goto done; } if ((data[1] < 0) || (data[1] > MLAN_SCAN_MODE_ANY)) { PRINTM(MERROR, "Invalid argument for scan mode\n"); ret = -EINVAL; goto done; } if ((data[2] < 0) || (data[2] > MAX_PROBES)) { PRINTM(MERROR, "Invalid argument for scan probes\n"); ret = -EINVAL; goto done; } if (((data[3] < 0) || (data[3] > MRVDRV_MAX_ACTIVE_SCAN_CHAN_TIME)) || ((data[4] < 0) || (data[4] > MRVDRV_MAX_ACTIVE_SCAN_CHAN_TIME)) || ((data[5] < 0) || (data[5] > MRVDRV_MAX_PASSIVE_SCAN_CHAN_TIME))) { PRINTM(MERROR, "Invalid argument for scan time\n"); ret = -EINVAL; goto done; } if ((data[6] < 0) || (data[6] > 1)) { PRINTM(MERROR, "Invalid argument for extended scan\n"); ret = -EINVAL; goto done; } req->action = MLAN_ACT_SET; moal_memcpy_ext(priv->phandle, &scan->param.scan_cfg, data, sizeof(data), sizeof(scan->param.scan_cfg)); } else req->action = MLAN_ACT_GET; status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto done; } if (!data_length) { moal_memcpy_ext(priv->phandle, data, &scan->param.scan_cfg, sizeof(scan->param.scan_cfg), sizeof(data)); if (copy_to_user(wrq->u.data.pointer, data, sizeof(data))) { ret = -EFAULT; goto done; } wrq->u.data.length = arg_len; } done: if (status != MLAN_STATUS_PENDING) kfree(req); LEAVE(); return ret; } /** * @brief Set/Get PS configuration parameters * * @param priv A pointer to moal_private structure * @param wrq A pointer to iwreq structure * * @return 0 --success, otherwise fail */ static int woal_set_get_ps_cfg(moal_private *priv, struct iwreq *wrq) { int data[7], copy_len, ret = 0; mlan_ds_pm_cfg *pm_cfg = NULL; mlan_ioctl_req *req = NULL; int allowed = 3; int i = 3; int data_length = wrq->u.data.length; mlan_status status = MLAN_STATUS_SUCCESS; ENTER(); allowed++; /* For beacon missing timeout parameter */ allowed += 2; /* For delay to PS and PS mode parameters */ copy_len = MIN(sizeof(data), sizeof(int) * data_length); req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_pm_cfg)); if (req == NULL) { ret = -ENOMEM; goto done; } if (data_length > allowed) { ret = -EINVAL; goto done; } pm_cfg = (mlan_ds_pm_cfg *)req->pbuf; pm_cfg->sub_command = MLAN_OID_PM_CFG_PS_CFG; req->req_id = MLAN_IOCTL_PM_CFG; memset(data, 0, sizeof(data)); if (data_length) { if (copy_from_user(data, wrq->u.data.pointer, copy_len)) { PRINTM(MERROR, "Copy from user failed\n"); ret = -EFAULT; goto done; } if ((data[0] < PS_NULL_DISABLE)) { PRINTM(MERROR, "Invalid argument for PS null interval\n"); ret = -EINVAL; goto done; } if ((data[1] != MRVDRV_IGNORE_MULTIPLE_DTIM) && (data[1] != MRVDRV_MATCH_CLOSEST_DTIM) && ((data[1] < MRVDRV_MIN_MULTIPLE_DTIM) || (data[1] > MRVDRV_MAX_MULTIPLE_DTIM))) { PRINTM(MERROR, "Invalid argument for multiple DTIM\n"); ret = -EINVAL; goto done; } if ((data[2] < MRVDRV_MIN_LISTEN_INTERVAL) && (data[2] != MRVDRV_LISTEN_INTERVAL_DISABLE)) { PRINTM(MERROR, "Invalid argument for listen interval\n"); ret = -EINVAL; goto done; } if ((data[i] != DISABLE_BCN_MISS_TO) && ((data[i] < MIN_BCN_MISS_TO) || (data[i] > MAX_BCN_MISS_TO))) { PRINTM(MERROR, "Invalid argument for beacon miss timeout\n"); ret = -EINVAL; goto done; } i++; if (data_length < allowed - 1) data[i] = DELAY_TO_PS_UNCHANGED; else if ((data[i] < MIN_DELAY_TO_PS) || (data[i] > MAX_DELAY_TO_PS)) { PRINTM(MERROR, "Invalid argument for delay to PS\n"); ret = -EINVAL; goto done; } i++; if ((data[i] != PS_MODE_UNCHANGED) && (data[i] != PS_MODE_AUTO) && (data[i] != PS_MODE_POLL) && (data[i] != PS_MODE_NULL)) { PRINTM(MERROR, "Invalid argument for PS mode\n"); ret = -EINVAL; goto done; } i++; req->action = MLAN_ACT_SET; moal_memcpy_ext(priv->phandle, &pm_cfg->param.ps_cfg, data, sizeof(data), sizeof(pm_cfg->param.ps_cfg)); } else req->action = MLAN_ACT_GET; status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto done; } moal_memcpy_ext(priv->phandle, data, &pm_cfg->param.ps_cfg, MIN((sizeof(int) * allowed), sizeof(pm_cfg->param.ps_cfg)), sizeof(data)); if (copy_to_user(wrq->u.data.pointer, data, sizeof(int) * allowed)) { ret = -EFAULT; goto done; } wrq->u.data.length = allowed; if (req->action == MLAN_ACT_SET) { pm_cfg = (mlan_ds_pm_cfg *)req->pbuf; pm_cfg->sub_command = MLAN_OID_PM_CFG_IEEE_PS; pm_cfg->param.ps_mode = 1; req->req_id = MLAN_IOCTL_PM_CFG; req->action = MLAN_ACT_SET; status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); } done: if (status != MLAN_STATUS_PENDING) kfree(req); LEAVE(); return ret; } /** * @brief Private IOCTL entry to send an ADDTS TSPEC * * Receive a ADDTS command from the application. The command structure * contains a TSPEC and timeout in milliseconds. The timeout is performed * in the firmware after the ADDTS command frame is sent. * * The TSPEC is received in the API as an opaque block. The firmware will * send the entire data block, including the bytes after the TSPEC. This * is done to allow extra IEs to be packaged with the TSPEC in the ADDTS * action frame. * * The IOCTL structure contains two return fields: * - The firmware command result, which indicates failure and timeouts * - The IEEE Status code which contains the corresponding value from * any ADDTS response frame received. * * In addition, the opaque TSPEC data block passed in is replaced with the * TSPEC received in the ADDTS response frame. In case of failure, the * AP may modify the TSPEC on return and in the case of success, the * medium time is returned as calculated by the AP. Along with the TSPEC, * any IEs that are sent in the ADDTS response are also returned and can be * parsed using the IOCTL length as an indicator of extra elements. * * The return value to the application layer indicates a driver execution * success or failure. A successful return could still indicate a firmware * failure or AP negotiation failure via the commandResult field copied * back to the application. * * @param priv Pointer to the mlan_private driver data struct * @param wrq A pointer to iwreq structure containing the * wlan_ioctl_wmm_addts_req_t struct for this ADDTS request * * @return 0 if successful; IOCTL error code otherwise */ static int woal_wmm_addts_req_ioctl(moal_private *priv, struct iwreq *wrq) { mlan_ioctl_req *req = NULL; mlan_ds_wmm_cfg *cfg = NULL; wlan_ioctl_wmm_addts_req_t addts_ioctl; int ret = 0; mlan_status status = MLAN_STATUS_SUCCESS; ENTER(); req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_wmm_cfg)); if (req == NULL) { ret = -ENOMEM; goto done; } req->req_id = MLAN_IOCTL_WMM_CFG; cfg = (mlan_ds_wmm_cfg *)req->pbuf; cfg->sub_command = MLAN_OID_WMM_CFG_ADDTS; memset(&addts_ioctl, 0x00, sizeof(addts_ioctl)); if (wrq->u.data.length) { if (copy_from_user(&addts_ioctl, wrq->u.data.pointer, MIN(wrq->u.data.length, sizeof(addts_ioctl)))) { PRINTM(MERROR, "TSPEC: ADDTS copy from user failed\n"); ret = -EFAULT; goto done; } cfg->param.addts.timeout = addts_ioctl.timeout_ms; cfg->param.addts.ie_data_len = addts_ioctl.ie_data_len; if (cfg->param.addts.ie_data_len > sizeof(cfg->param.addts.ie_data)) { PRINTM(MERROR, "IE data length too large\n"); ret = -EFAULT; goto done; } moal_memcpy_ext(priv->phandle, cfg->param.addts.ie_data, addts_ioctl.ie_data, cfg->param.addts.ie_data_len, sizeof(cfg->param.addts.ie_data)); status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto done; } addts_ioctl.cmd_result = cfg->param.addts.result; addts_ioctl.ieee_status_code = (t_u8)cfg->param.addts.status_code; addts_ioctl.ie_data_len = cfg->param.addts.ie_data_len; moal_memcpy_ext(priv->phandle, addts_ioctl.ie_data, cfg->param.addts.ie_data, cfg->param.addts.ie_data_len, sizeof(addts_ioctl.ie_data)); wrq->u.data.length = (sizeof(addts_ioctl) - sizeof(addts_ioctl.ie_data) + cfg->param.addts.ie_data_len); if (copy_to_user(wrq->u.data.pointer, &addts_ioctl, wrq->u.data.length)) { PRINTM(MERROR, "TSPEC: ADDTS copy to user failed\n"); ret = -EFAULT; goto done; } } done: if (status != MLAN_STATUS_PENDING) kfree(req); LEAVE(); return ret; } /** * @brief Private IOCTL entry to send a DELTS TSPEC * * Receive a DELTS command from the application. The command structure * contains a TSPEC and reason code along with space for a command result * to be returned. The information is packaged is sent to the wlan_cmd.c * firmware command prep and send routines for execution in the firmware. * * The reason code is not used for WMM implementations but is indicated in * the 802.11e specification. * * The return value to the application layer indicates a driver execution * success or failure. A successful return could still indicate a firmware * failure via the cmd_result field copied back to the application. * * @param priv Pointer to the mlan_private driver data struct * @param wrq A pointer to iwreq structure containing the * wlan_ioctl_wmm_delts_req_t struct for this DELTS request * * @return 0 if successful; IOCTL error code otherwise */ static int woal_wmm_delts_req_ioctl(moal_private *priv, struct iwreq *wrq) { mlan_ioctl_req *req = NULL; mlan_ds_wmm_cfg *cfg = NULL; wlan_ioctl_wmm_delts_req_t delts_ioctl; int ret = 0; mlan_status status = MLAN_STATUS_SUCCESS; ENTER(); req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_wmm_cfg)); if (req == NULL) { ret = -ENOMEM; goto done; } req->req_id = MLAN_IOCTL_WMM_CFG; cfg = (mlan_ds_wmm_cfg *)req->pbuf; cfg->sub_command = MLAN_OID_WMM_CFG_DELTS; memset(&delts_ioctl, 0x00, sizeof(delts_ioctl)); if (wrq->u.data.length) { if (copy_from_user(&delts_ioctl, wrq->u.data.pointer, MIN(wrq->u.data.length, sizeof(delts_ioctl)))) { PRINTM(MERROR, "TSPEC: DELTS copy from user failed\n"); ret = -EFAULT; goto done; } cfg->param.delts.status_code = (t_u32)delts_ioctl.ieee_reason_code; cfg->param.delts.ie_data_len = (t_u8)delts_ioctl.ie_data_len; if ((cfg->param.delts.ie_data_len) > sizeof(cfg->param.delts.ie_data)) { PRINTM(MERROR, "IE data length too large\n"); ret = -EFAULT; goto done; } moal_memcpy_ext(priv->phandle, cfg->param.delts.ie_data, delts_ioctl.ie_data, cfg->param.delts.ie_data_len, sizeof(cfg->param.delts.ie_data)); status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto done; } /* Return the firmware command result back to the application * layer */ delts_ioctl.cmd_result = cfg->param.delts.result; wrq->u.data.length = sizeof(delts_ioctl); if (copy_to_user(wrq->u.data.pointer, &delts_ioctl, wrq->u.data.length)) { PRINTM(MERROR, "TSPEC: DELTS copy to user failed\n"); ret = -EFAULT; goto done; } } done: if (status != MLAN_STATUS_PENDING) kfree(req); LEAVE(); return ret; } /** * @brief Private IOCTL entry to get/set a specified AC Queue's parameters * * Receive a AC Queue configuration command which is used to get, set, or * default the parameters associated with a specific WMM AC Queue. * * @param priv Pointer to the mlan_private driver data struct * @param wrq A pointer to iwreq structure containing the * wlan_ioctl_wmm_queue_config_t struct * * @return 0 if successful; IOCTL error code otherwise */ static int woal_wmm_queue_config_ioctl(moal_private *priv, struct iwreq *wrq) { mlan_ioctl_req *req = NULL; mlan_ds_wmm_cfg *pwmm = NULL; mlan_ds_wmm_queue_config *pqcfg = NULL; wlan_ioctl_wmm_queue_config_t qcfg_ioctl; int ret = 0; mlan_status status = MLAN_STATUS_SUCCESS; ENTER(); req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_wmm_cfg)); if (req == NULL) { ret = -ENOMEM; goto done; } req->req_id = MLAN_IOCTL_WMM_CFG; pwmm = (mlan_ds_wmm_cfg *)req->pbuf; pwmm->sub_command = MLAN_OID_WMM_CFG_QUEUE_CONFIG; memset(&qcfg_ioctl, 0x00, sizeof(qcfg_ioctl)); pqcfg = (mlan_ds_wmm_queue_config *)&pwmm->param.q_cfg; if (wrq->u.data.length) { if (copy_from_user(&qcfg_ioctl, wrq->u.data.pointer, MIN(wrq->u.data.length, sizeof(qcfg_ioctl)))) { PRINTM(MERROR, "QCONFIG: copy from user failed\n"); ret = -EFAULT; goto done; } pqcfg->action = qcfg_ioctl.action; pqcfg->access_category = qcfg_ioctl.access_category; pqcfg->msdu_lifetime_expiry = qcfg_ioctl.msdu_lifetime_expiry; status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto done; } memset(&qcfg_ioctl, 0x00, sizeof(qcfg_ioctl)); qcfg_ioctl.action = pqcfg->action; qcfg_ioctl.access_category = pqcfg->access_category; qcfg_ioctl.msdu_lifetime_expiry = pqcfg->msdu_lifetime_expiry; wrq->u.data.length = sizeof(qcfg_ioctl); if (copy_to_user(wrq->u.data.pointer, &qcfg_ioctl, wrq->u.data.length)) { PRINTM(MERROR, "QCONFIG: copy to user failed\n"); ret = -EFAULT; goto done; } } done: if (status != MLAN_STATUS_PENDING) kfree(req); LEAVE(); return ret; } /** * @brief Private IOCTL entry to get and start/stop queue stats on a WMM AC * * Receive a AC Queue statistics command from the application for a specific * WMM AC. The command can: * - Turn stats on * - Turn stats off * - Collect and clear the stats * * @param priv Pointer to the moal_private driver data struct * @param wrq A pointer to iwreq structure containing the * wlan_ioctl_wmm_queue_stats_t struct * * @return 0 if successful; IOCTL error code otherwise */ static int woal_wmm_queue_stats_ioctl(moal_private *priv, struct iwreq *wrq) { mlan_ioctl_req *req = NULL; mlan_ds_wmm_cfg *pwmm = NULL; mlan_ds_wmm_queue_stats *pqstats = NULL; wlan_ioctl_wmm_queue_stats_t qstats_ioctl; int ret = 0; mlan_status status = MLAN_STATUS_SUCCESS; ENTER(); req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_wmm_cfg)); if (req == NULL) { ret = -ENOMEM; goto done; } req->req_id = MLAN_IOCTL_WMM_CFG; pwmm = (mlan_ds_wmm_cfg *)req->pbuf; pwmm->sub_command = MLAN_OID_WMM_CFG_QUEUE_STATS; memset(&qstats_ioctl, 0x00, sizeof(qstats_ioctl)); pqstats = (mlan_ds_wmm_queue_stats *)&pwmm->param.q_stats; if (wrq->u.data.length) { if (copy_from_user(&qstats_ioctl, wrq->u.data.pointer, MIN(wrq->u.data.length, sizeof(qstats_ioctl)))) { PRINTM(MERROR, "QSTATS: copy from user failed\n"); ret = -EFAULT; goto done; } moal_memcpy_ext(priv->phandle, (void *)pqstats, (void *)&qstats_ioctl, sizeof(qstats_ioctl), sizeof(qstats_ioctl)); PRINTM(MINFO, "QSTATS: IOCTL [%d,%d]\n", qstats_ioctl.action, qstats_ioctl.user_priority); status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto done; } memset(&qstats_ioctl, 0x00, sizeof(qstats_ioctl)); moal_memcpy_ext(priv->phandle, (void *)&qstats_ioctl, (void *)pqstats, sizeof(qstats_ioctl), sizeof(qstats_ioctl)); wrq->u.data.length = sizeof(qstats_ioctl); if (copy_to_user(wrq->u.data.pointer, &qstats_ioctl, wrq->u.data.length)) { PRINTM(MERROR, "QSTATS: copy to user failed\n"); ret = -EFAULT; goto done; } } done: if (status != MLAN_STATUS_PENDING) kfree(req); LEAVE(); return ret; } /** * @brief Private IOCTL entry to get the status of the WMM queues * * Return the following information for each WMM AC: * - WMM IE Acm Required * - Firmware Flow Required * - Firmware Flow Established * - Firmware Queue Enabled * - Firmware Delivery Enabled * - Firmware Trigger Enabled * * @param priv Pointer to the moal_private driver data struct * @param wrq A pointer to iwreq structure containing the * wlan_ioctl_wmm_queue_status_t struct for request * * @return 0 if successful; IOCTL error code otherwise */ static int woal_wmm_queue_status_ioctl(moal_private *priv, struct iwreq *wrq) { mlan_ioctl_req *req = NULL; mlan_ds_wmm_cfg *pwmm = NULL; wlan_ioctl_wmm_queue_status_t qstatus_ioctl; int ret = 0; mlan_status status = MLAN_STATUS_SUCCESS; ENTER(); req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_wmm_cfg)); if (req == NULL) { ret = -ENOMEM; goto done; } req->req_id = MLAN_IOCTL_WMM_CFG; pwmm = (mlan_ds_wmm_cfg *)req->pbuf; pwmm->sub_command = MLAN_OID_WMM_CFG_QUEUE_STATUS; if (wrq->u.data.length == sizeof(qstatus_ioctl)) { status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto done; } memset(&qstatus_ioctl, 0x00, sizeof(qstatus_ioctl)); moal_memcpy_ext(priv->phandle, (void *)&qstatus_ioctl, (void *)&pwmm->param.q_status, sizeof(qstatus_ioctl), sizeof(qstatus_ioctl)); wrq->u.data.length = sizeof(qstatus_ioctl); } else { wrq->u.data.length = 0; } if (copy_to_user(wrq->u.data.pointer, &qstatus_ioctl, wrq->u.data.length)) { PRINTM(MERROR, "QSTATUS: copy to user failed\n"); ret = -EFAULT; goto done; } done: if (status != MLAN_STATUS_PENDING) kfree(req); LEAVE(); return ret; } /** * @brief Private IOCTL entry to get the status of the WMM Traffic Streams * * @param priv Pointer to the moal_private driver data struct * @param wrq A pointer to iwreq structure containing the * wlan_ioctl_wmm_ts_status_t struct for request * * @return 0 if successful; IOCTL error code otherwise */ static int woal_wmm_ts_status_ioctl(moal_private *priv, struct iwreq *wrq) { mlan_ioctl_req *req = NULL; mlan_ds_wmm_cfg *pwmm = NULL; wlan_ioctl_wmm_ts_status_t ts_status_ioctl; int ret = 0; mlan_status status = MLAN_STATUS_SUCCESS; ENTER(); req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_wmm_cfg)); if (req == NULL) { ret = -ENOMEM; goto done; } req->req_id = MLAN_IOCTL_WMM_CFG; pwmm = (mlan_ds_wmm_cfg *)req->pbuf; pwmm->sub_command = MLAN_OID_WMM_CFG_TS_STATUS; memset(&ts_status_ioctl, 0x00, sizeof(ts_status_ioctl)); if (wrq->u.data.length == sizeof(ts_status_ioctl)) { if (copy_from_user(&ts_status_ioctl, wrq->u.data.pointer, MIN(wrq->u.data.length, sizeof(ts_status_ioctl)))) { PRINTM(MERROR, "TS_STATUS: copy from user failed\n"); ret = -EFAULT; goto done; } memset(&pwmm->param.ts_status, 0x00, sizeof(ts_status_ioctl)); pwmm->param.ts_status.tid = ts_status_ioctl.tid; status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto done; } memset(&ts_status_ioctl, 0x00, sizeof(ts_status_ioctl)); moal_memcpy_ext(priv->phandle, (void *)&ts_status_ioctl, (void *)&pwmm->param.ts_status, sizeof(ts_status_ioctl), sizeof(ts_status_ioctl)); wrq->u.data.length = sizeof(ts_status_ioctl); } else { wrq->u.data.length = 0; } if (copy_to_user(wrq->u.data.pointer, &ts_status_ioctl, wrq->u.data.length)) { PRINTM(MERROR, "TS_STATUS: copy to user failed\n"); ret = -EFAULT; goto done; } done: if (status != MLAN_STATUS_PENDING) kfree(req); LEAVE(); return ret; } /** * @brief Private IOCTL entry to get the By-passed TX packet from upper layer * * @param priv Pointer to the moal_private driver data struct * @param wrq A pointer to iwreq structure containing the packet * * @return 0 if successful; IOCTL error code otherwise */ static int woal_bypassed_packet_ioctl(moal_private *priv, struct iwreq *wrq) { int ret = 0; struct sk_buff *skb = NULL; struct ethhdr *eth; t_u16 moreLen = 0, copyLen = 0; ENTER(); #define MLAN_BYPASS_PKT_EXTRA_OFFSET (4) copyLen = wrq->u.data.length; moreLen = MLAN_MIN_DATA_HEADER_LEN + MLAN_BYPASS_PKT_EXTRA_OFFSET + sizeof(mlan_buffer); skb = alloc_skb(copyLen + moreLen, GFP_KERNEL); if (skb == NULL) { PRINTM(MERROR, "kmalloc no memory !!\n"); LEAVE(); return -ENOMEM; } skb_reserve(skb, moreLen); if (copy_from_user(skb_put(skb, copyLen), wrq->u.data.pointer, copyLen)) { PRINTM(MERROR, "PortBlock: copy from user failed\n"); dev_kfree_skb_any(skb); ret = -EFAULT; goto done; } eth = (struct ethhdr *)skb->data; eth->h_proto = __constant_htons(eth->h_proto); skb->dev = priv->netdev; HEXDUMP("Bypass TX Data", skb->data, MIN(skb->len, 100)); woal_hard_start_xmit(skb, priv->netdev); done: LEAVE(); return ret; } /** * @brief Set/Get auth type * * @param priv Pointer to the moal_private driver data struct * @param wrq A pointer to iwreq structure * * @return 0 --success, otherwise fail */ static int woal_auth_type(moal_private *priv, struct iwreq *wrq) { int auth_type; t_u32 auth_mode; int ret = 0; ENTER(); if (wrq->u.data.length == 0) { if (MLAN_STATUS_SUCCESS != woal_get_auth_mode(priv, MOAL_IOCTL_WAIT, &auth_mode)) { ret = -EFAULT; goto done; } auth_type = auth_mode; if (copy_to_user(wrq->u.data.pointer, &auth_type, sizeof(auth_type))) { PRINTM(MERROR, "Copy to user failed\n"); ret = -EFAULT; goto done; } wrq->u.data.length = 1; } else { if (copy_from_user(&auth_type, wrq->u.data.pointer, sizeof(auth_type))) { PRINTM(MERROR, "Copy from user failed\n"); ret = -EFAULT; goto done; } PRINTM(MINFO, "SET: auth_type %d\n", auth_type); if (((auth_type < MLAN_AUTH_MODE_OPEN) || (auth_type > MLAN_AUTH_MODE_SHARED)) && (auth_type != MLAN_AUTH_MODE_AUTO)) { ret = -EINVAL; goto done; } auth_mode = auth_type; if (MLAN_STATUS_SUCCESS != woal_set_auth_mode(priv, MOAL_IOCTL_WAIT, auth_mode)) { ret = -EFAULT; goto done; } } done: LEAVE(); return ret; } /** * @brief Set/Get Port Control mode * * @param priv Pointer to the moal_private driver data struct * @param wrq A pointer to iwreq structure * * @return 0 --success, otherwise fail */ static int woal_port_ctrl(moal_private *priv, struct iwreq *wrq) { mlan_ioctl_req *req = NULL; mlan_ds_sec_cfg *sec = NULL; int ret = 0; mlan_status status = MLAN_STATUS_SUCCESS; ENTER(); /* Allocate an IOCTL request buffer */ req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg)); if (req == NULL) { ret = -ENOMEM; goto done; } /* Fill request buffer */ sec = (mlan_ds_sec_cfg *)req->pbuf; sec->sub_command = MLAN_OID_SEC_CFG_PORT_CTRL_ENABLED; req->req_id = MLAN_IOCTL_SEC_CFG; if (wrq->u.data.length) { if (copy_from_user(&sec->param.port_ctrl_enabled, wrq->u.data.pointer, sizeof(int)) != 0) { PRINTM(MERROR, "port_ctrl:Copy from user failed\n"); ret = -EFAULT; goto done; } req->action = MLAN_ACT_SET; } else { req->action = MLAN_ACT_GET; } /* Send IOCTL request to MLAN */ status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto done; } if (!wrq->u.data.length) { if (copy_to_user(wrq->u.data.pointer, &sec->param.port_ctrl_enabled, sizeof(int))) { PRINTM(MERROR, "port_ctrl:Copy to user failed\n"); ret = -EFAULT; goto done; } wrq->u.data.length = 1; } done: if (status != MLAN_STATUS_PENDING) kfree(req); LEAVE(); return ret; } /** * @brief Set/Get DFS Testing settings * * @param priv Pointer to the moal_private driver data struct * @param wrq A pointer to iwreq structure * * @return 0 --success, otherwise fail */ static int woal_dfs_testing(moal_private *priv, struct iwreq *wrq) { mlan_ioctl_req *req = NULL; mlan_ds_11h_cfg *ds_11hcfg = NULL; int ret = 0; int data[4], copy_len; int data_length = wrq->u.data.length; mlan_status status = MLAN_STATUS_SUCCESS; ENTER(); copy_len = MIN(sizeof(data), sizeof(int) * data_length); /* Allocate an IOCTL request buffer */ req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11h_cfg)); if (req == NULL) { ret = -ENOMEM; goto done; } /* Fill request buffer */ ds_11hcfg = (mlan_ds_11h_cfg *)req->pbuf; ds_11hcfg->sub_command = MLAN_OID_11H_DFS_TESTING; req->req_id = MLAN_IOCTL_11H_CFG; if (!data_length) { req->action = MLAN_ACT_GET; } else if (data_length == 4) { if (copy_from_user(data, wrq->u.data.pointer, copy_len)) { PRINTM(MERROR, "Copy from user failed\n"); ret = -EFAULT; goto done; } if ((unsigned)data[0] > 1800) { PRINTM(MERROR, "The maximum user CAC is 1800 sec.\n"); ret = -EINVAL; goto done; } if ((unsigned)data[1] > 0xFFFF) { PRINTM(MERROR, "The maximum user NOP is 65535 sec.\n"); ret = -EINVAL; goto done; } if ((unsigned)data[3] > 0xFF) { PRINTM(MERROR, "The maximum user fixed channel is 255.\n"); ret = -EINVAL; goto done; } ds_11hcfg->param.dfs_testing.usr_cac_period_msec = (t_u16)data[0]; ds_11hcfg->param.dfs_testing.usr_nop_period_sec = (t_u16)data[1]; ds_11hcfg->param.dfs_testing.usr_no_chan_change = data[2] ? 1 : 0; ds_11hcfg->param.dfs_testing.usr_fixed_new_chan = (t_u8)data[3]; priv->phandle->cac_period_jiffies = (t_u16)data[0] * HZ; req->action = MLAN_ACT_SET; } else { PRINTM(MERROR, "Invalid number of args!\n"); ret = -EINVAL; goto done; } /* Send IOCTL request to MLAN */ status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto done; } if (!data_length) { data[0] = ds_11hcfg->param.dfs_testing.usr_cac_period_msec; data[1] = ds_11hcfg->param.dfs_testing.usr_nop_period_sec; data[2] = ds_11hcfg->param.dfs_testing.usr_no_chan_change; data[3] = ds_11hcfg->param.dfs_testing.usr_fixed_new_chan; if (copy_to_user(wrq->u.data.pointer, &data, sizeof(int) * 4)) { PRINTM(MERROR, "Copy to user failed\n"); ret = -EFAULT; goto done; } wrq->u.data.length = 4; } done: if (status != MLAN_STATUS_PENDING) kfree(req); LEAVE(); return ret; } /** * @brief Set/Get Mgmt Frame passthru mask * * @param priv A pointer to moal_private structure * @param wrq A pointer to iwreq structure * * @return 0 -- success, otherwise fail */ static int woal_mgmt_frame_passthru_ctrl(moal_private *priv, struct iwreq *wrq) { int ret = 0, data_length = wrq->u.data.length; mlan_ioctl_req *req = NULL; mlan_ds_misc_cfg *mgmt_cfg = NULL; int mask = 0; mlan_status status = MLAN_STATUS_SUCCESS; ENTER(); if (data_length > 1) { PRINTM(MERROR, "Invalid no of arguments!\n"); ret = -EINVAL; goto done; } /* Allocate an IOCTL request buffer */ req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); if (req == NULL) { ret = -ENOMEM; goto done; } /* Fill request buffer */ mgmt_cfg = (mlan_ds_misc_cfg *)req->pbuf; req->req_id = MLAN_IOCTL_MISC_CFG; mgmt_cfg->sub_command = MLAN_OID_MISC_RX_MGMT_IND; if (data_length) { /* SET */ if (copy_from_user(&mask, wrq->u.data.pointer, sizeof(int))) { PRINTM(MERROR, "copy from user failed\n"); ret = -EFAULT; goto done; } mgmt_cfg->param.mgmt_subtype_mask = mask; req->action = MLAN_ACT_SET; } else { req->action = MLAN_ACT_GET; } /* Send IOCTL request to MLAN */ status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto done; } mask = mgmt_cfg->param.mgmt_subtype_mask; if (copy_to_user(wrq->u.data.pointer, &mask, sizeof(int))) { ret = -EFAULT; goto done; } wrq->u.data.length = 1; done: if (status != MLAN_STATUS_PENDING) kfree(req); LEAVE(); return ret; } /** * @brief Set/Get CFP table codes * * @param priv Pointer to the moal_private driver data struct * @param wrq A pointer to iwreq structure * * @return 0 --success, otherwise fail */ static int woal_cfp_code(moal_private *priv, struct iwreq *wrq) { int ret = 0; int data[2], copy_len; int data_length = wrq->u.data.length; mlan_ioctl_req *req = NULL; mlan_ds_misc_cfg *misc_cfg = NULL; mlan_ds_misc_cfp_code *cfp_code = NULL; mlan_status status = MLAN_STATUS_SUCCESS; ENTER(); if (data_length > 2) { PRINTM(MERROR, "Invalid number of argument!\n"); ret = -EINVAL; goto done; } copy_len = MIN(sizeof(data), sizeof(int) * data_length); /* Allocate an IOCTL request buffer */ req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); if (req == NULL) { ret = -ENOMEM; goto done; } /* Fill request buffer */ misc_cfg = (mlan_ds_misc_cfg *)req->pbuf; cfp_code = &misc_cfg->param.cfp_code; misc_cfg->sub_command = MLAN_OID_MISC_CFP_CODE; req->req_id = MLAN_IOCTL_MISC_CFG; if (!data_length) { req->action = MLAN_ACT_GET; } else { if (copy_from_user(data, wrq->u.data.pointer, copy_len)) { PRINTM(MERROR, "Copy from user failed\n"); ret = -EFAULT; goto done; } cfp_code->cfp_code_bg = data[0]; if (data_length == 2) cfp_code->cfp_code_a = data[1]; req->action = MLAN_ACT_SET; } /* Send IOCTL request to MLAN */ status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto done; } if (!data_length) { data[0] = cfp_code->cfp_code_bg; data[1] = cfp_code->cfp_code_a; data_length = 2; if (copy_to_user(wrq->u.data.pointer, &data, sizeof(int) * data_length)) { PRINTM(MERROR, "Copy to user failed\n"); ret = -EFAULT; goto done; } wrq->u.data.length = data_length; } done: if (status != MLAN_STATUS_PENDING) kfree(req); LEAVE(); return ret; } /** * @brief Set/Get Tx/Rx antenna * * @param priv A pointer to moal_private structure * @param wrq A pointer to iwreq structure * * @return 0 --success, otherwise fail */ static int woal_set_get_tx_rx_ant(moal_private *priv, struct iwreq *wrq) { int ret = 0; mlan_ds_radio_cfg *radio = NULL; mlan_ioctl_req *req = NULL; int data[3] = {0}; mlan_status status = MLAN_STATUS_SUCCESS; int copy_len; ENTER(); if (wrq->u.data.length > 2) { PRINTM(MERROR, "Invalid number of argument!\n"); ret = -EFAULT; goto done; } if (wrq->u.data.length * sizeof(int) > sizeof(data)) { PRINTM(MERROR, "Too many arguments\n"); ret = -EFAULT; goto done; } copy_len = MIN(sizeof(data), wrq->u.data.length * sizeof(int)); req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_radio_cfg)); if (req == NULL) { ret = -ENOMEM; goto done; } radio = (mlan_ds_radio_cfg *)req->pbuf; radio->sub_command = MLAN_OID_ANT_CFG; req->req_id = MLAN_IOCTL_RADIO_CFG; if (wrq->u.data.length) { if (copy_from_user(data, wrq->u.data.pointer, copy_len)) { PRINTM(MERROR, "Copy from user failed\n"); ret = -EFAULT; goto done; } if (priv->phandle->feature_control & FEATURE_CTRL_STREAM_2X2) { radio->param.ant_cfg.tx_antenna = data[0]; radio->param.ant_cfg.rx_antenna = data[0]; if (wrq->u.data.length == 2) radio->param.ant_cfg.rx_antenna = data[1]; } else { radio->param.ant_cfg_1x1.antenna = data[0]; if (wrq->u.data.length == 2) radio->param.ant_cfg_1x1.evaluate_time = data[1]; } req->action = MLAN_ACT_SET; } else req->action = MLAN_ACT_GET; status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto done; } if (!wrq->u.data.length) { wrq->u.data.length = 1; if (priv->phandle->feature_control & FEATURE_CTRL_STREAM_2X2) { data[0] = radio->param.ant_cfg.tx_antenna; data[1] = radio->param.ant_cfg.rx_antenna; if (data[0] && data[1] && (data[0] != data[1])) wrq->u.data.length = 2; } else { data[0] = (int)radio->param.ant_cfg_1x1.antenna; data[1] = (int)radio->param.ant_cfg_1x1.evaluate_time; data[2] = (int)radio->param.ant_cfg_1x1.current_antenna; if (data[0] == 0xffff && data[2] > 0) wrq->u.data.length = 3; else if (data[0] == 0xffff) wrq->u.data.length = 2; } if (copy_to_user(wrq->u.data.pointer, data, wrq->u.data.length * sizeof(int))) { ret = -EFAULT; goto done; } } done: if (status != MLAN_STATUS_PENDING) kfree(req); LEAVE(); return ret; } /** * @brief Configure gpio independent reset * * @param priv A pointer to moal_private structure * @param req A pointer to ifreq structure * * @return 0 --success, otherwise fail */ static int woal_ind_rst_ioctl(moal_private *priv, struct iwreq *wrq) { int data[2], data_length = wrq->u.data.length, copy_len; int ret = 0; mlan_ds_misc_cfg *misc = NULL; mlan_ioctl_req *req = NULL; mlan_status status = MLAN_STATUS_SUCCESS; ENTER(); if (sizeof(int) * wrq->u.data.length > sizeof(data)) { PRINTM(MERROR, "Too many arguments\n"); ret = -EINVAL; goto done; } copy_len = MIN(sizeof(data), sizeof(int) * data_length); req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); if (req == NULL) { ret = -ENOMEM; goto done; } misc = (mlan_ds_misc_cfg *)req->pbuf; memset(misc, 0, sizeof(mlan_ds_misc_cfg)); misc->sub_command = MLAN_OID_MISC_IND_RST_CFG; req->req_id = MLAN_IOCTL_MISC_CFG; if (data_length == 0) { req->action = MLAN_ACT_GET; } else if ((data_length == 1) || (data_length == 2)) { req->action = MLAN_ACT_SET; if (copy_from_user(data, wrq->u.data.pointer, copy_len)) { /* copy_from_user failed */ PRINTM(MERROR, "S_PARAMS: copy from user failed\n"); ret = -EINVAL; goto done; } /* ir_mode */ if (data[0] < 0 || data[0] > 2) { PRINTM(MERROR, "Invalid ir mode parameter (0/1/2)!\n"); ret = -EINVAL; goto done; } misc->param.ind_rst_cfg.ir_mode = data[0]; /* gpio_pin */ if (data_length == 2) { if ((data[1] != 0xFF) && (data[1] < 0)) { PRINTM(MERROR, "Invalid gpio pin no !\n"); ret = -EINVAL; goto done; } misc->param.ind_rst_cfg.gpio_pin = data[1]; } } else { ret = -EINVAL; goto done; } status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); if (status != MLAN_STATUS_SUCCESS) { ret = -EFAULT; goto done; } data[0] = misc->param.ind_rst_cfg.ir_mode; data[1] = misc->param.ind_rst_cfg.gpio_pin; wrq->u.data.length = 2; if (copy_to_user(wrq->u.data.pointer, data, sizeof(int) * wrq->u.data.length)) { PRINTM(MERROR, "QCONFIG: copy to user failed\n"); ret = -EFAULT; goto done; } done: if (status != MLAN_STATUS_PENDING) kfree(req); LEAVE(); return ret; } /******************************************************** Global Functions ********************************************************/ /** * @brief ioctl function - entry point * * @param dev A pointer to net_device structure * @param req A pointer to ifreq structure * @param cmd Command * * @return 0 --success, otherwise fail */ int woal_wext_do_ioctl(struct net_device *dev, struct ifreq *req, int cmd) { moal_private *priv = (moal_private *)netdev_priv(dev); struct iwreq *wrq = (struct iwreq *)req; int ret = 0; if (!IS_STA_WEXT(priv->phandle->params.cfg80211_wext)) return -EOPNOTSUPP; ENTER(); PRINTM(MINFO, "woal_wext_do_ioctl: ioctl cmd = 0x%x\n", cmd); switch (cmd) { case WOAL_SETONEINT_GETWORDCHAR: switch (wrq->u.data.flags) { case WOAL_VERSION: /* Get driver version */ ret = woal_get_driver_version(priv, req); break; case WOAL_VEREXT: /* Get extended driver version */ ret = woal_get_driver_verext(priv, req); break; default: ret = -EOPNOTSUPP; break; } break; case WOAL_SETNONE_GETNONE: switch (wrq->u.data.flags) { case WOAL_WARMRESET: ret = woal_warm_reset(priv); break; #ifdef USB #ifdef CONFIG_USB_SUSPEND case WOAL_USB_SUSPEND: ret = woal_enter_usb_suspend(priv->phandle); break; case WOAL_USB_RESUME: ret = woal_exit_usb_suspend(priv->phandle); break; #endif /* CONFIG_USB_SUSPEND */ #endif case WOAL_11D_CLR_CHAN_TABLE: ret = woal_11d_clr_chan_table(priv, wrq); break; default: ret = -EOPNOTSUPP; break; } break; case WOAL_SETONEINT_GETONEINT: switch (wrq->u.data.flags) { case WOAL_SET_GET_TXRATE: ret = woal_set_get_txrate(priv, wrq); break; case WOAL_SET_GET_REGIONCODE: ret = woal_set_get_regioncode(priv, wrq); break; case WOAL_SET_RADIO: ret = woal_set_get_radio(priv, wrq); break; case WOAL_WMM_ENABLE: ret = woal_wmm_enable_ioctl(priv, wrq); break; case WOAL_11D_ENABLE: ret = woal_11d_enable_ioctl(priv, wrq); break; case WOAL_SET_GET_QOS_CFG: ret = woal_set_get_qos_cfg(priv, wrq); break; #if defined(REASSOCIATION) case WOAL_SET_GET_REASSOC: ret = woal_set_get_reassoc(priv, wrq); break; #endif /* REASSOCIATION */ case WOAL_TXBUF_CFG: ret = woal_txbuf_cfg(priv, wrq); break; #ifndef OPCHAN case WOAL_SET_GET_WWS_CFG: ret = woal_wws_cfg(priv, wrq); break; #endif case WOAL_SLEEP_PD: ret = woal_sleep_pd(priv, wrq); break; case WOAL_AUTH_TYPE: ret = woal_auth_type(priv, wrq); break; case WOAL_PORT_CTRL: ret = woal_port_ctrl(priv, wrq); break; #ifdef WIFI_DIRECT_SUPPORT #if defined(STA_SUPPORT) && defined(UAP_SUPPORT) case WOAL_SET_GET_BSS_ROLE: ret = woal_set_get_bss_role(priv, wrq); break; #endif #endif case WOAL_SET_GET_11H_LOCAL_PWR_CONSTRAINT: ret = woal_set_get_11h_local_pwr_constraint(priv, wrq); break; case WOAL_HT_STREAM_CFG: ret = woal_ht_stream_cfg_ioctl(priv, wrq); break; case WOAL_MAC_CONTROL: ret = woal_mac_control_ioctl(priv, wrq); break; case WOAL_THERMAL: ret = woal_thermal_ioctl(priv, wrq); break; case WOAL_CFG_HOTSPOT: ret = woal_cfg_hotspot(priv, wrq); break; default: ret = -EOPNOTSUPP; break; } break; case WOAL_SET_GET_SIXTEEN_INT: switch ((int)wrq->u.data.flags) { case WOAL_TX_POWERCFG: ret = woal_tx_power_cfg(priv, wrq); break; #ifdef DEBUG_LEVEL1 case WOAL_DRV_DBG: ret = woal_drv_dbg(priv, wrq); break; #endif case WOAL_BEACON_INTERVAL: ret = woal_beacon_interval(priv, wrq); break; case WOAL_SIGNAL: ret = woal_get_signal(priv, wrq); break; case WOAL_DEEP_SLEEP: ret = woal_deep_sleep_ioctl(priv, wrq); break; case WOAL_11N_TX_CFG: ret = woal_11n_tx_cfg(priv, wrq); break; case WOAL_11N_AMSDU_AGGR_CTRL: ret = woal_11n_amsdu_aggr_ctrl(priv, wrq); break; case WOAL_11N_HTCAP_CFG: ret = woal_11n_htcap_cfg(priv, wrq); break; case WOAL_PRIO_TBL: ret = woal_11n_prio_tbl(priv, wrq); break; case WOAL_ADDBA_UPDT: ret = woal_addba_para_updt(priv, wrq); break; case WOAL_ADDBA_REJECT: ret = woal_addba_reject(priv, wrq); break; case WOAL_TX_BF_CAP: ret = woal_tx_bf_cap_ioctl(priv, wrq); break; case WOAL_HS_CFG: ret = woal_hs_cfg(priv, wrq, MTRUE); break; case WOAL_HS_SETPARA: ret = woal_hs_setpara(priv, wrq); break; case WOAL_REG_READ_WRITE: ret = woal_reg_read_write(priv, wrq); break; case WOAL_INACTIVITY_TIMEOUT_EXT: ret = woal_inactivity_timeout_ext(priv, wrq); break; #ifdef SDIO case WOAL_SDIO_CLOCK: ret = woal_sdio_clock_ioctl(priv, wrq); break; case WOAL_CMD_52RDWR: ret = woal_cmd52rdwr_ioctl(priv, wrq); break; case WOAL_SDIO_MPA_CTRL: ret = woal_do_sdio_mpa_ctrl(priv, wrq); break; #endif case WOAL_BAND_CFG: ret = woal_band_cfg(priv, wrq); break; case WOAL_SCAN_CFG: ret = woal_set_get_scan_cfg(priv, wrq); break; case WOAL_PS_CFG: ret = woal_set_get_ps_cfg(priv, wrq); break; case WOAL_MEM_READ_WRITE: ret = woal_mem_read_write(priv, wrq); break; case WOAL_SLEEP_PARAMS: ret = woal_sleep_params_ioctl(priv, wrq); break; case WOAL_NET_MONITOR: ret = woal_net_monitor_ioctl(priv, wrq); break; case WOAL_DFS_TESTING: ret = woal_dfs_testing(priv, wrq); break; case WOAL_MGMT_FRAME_CTRL: ret = woal_mgmt_frame_passthru_ctrl(priv, wrq); break; case WOAL_CFP_CODE: ret = woal_cfp_code(priv, wrq); break; case WOAL_SET_GET_TX_RX_ANT: ret = woal_set_get_tx_rx_ant(priv, wrq); break; case WOAL_IND_RST_CFG: ret = woal_ind_rst_ioctl(priv, wrq); break; default: ret = -EINVAL; break; } break; case WOALGETLOG: ret = woal_get_log(priv, wrq); break; case WOAL_SET_GET_256_CHAR: switch (wrq->u.data.flags) { case WOAL_PASSPHRASE: ret = woal_passphrase(priv, wrq); break; case WOAL_GET_KEY: ret = woal_get_key_ioctl(priv, wrq); break; case WOAL_ASSOCIATE: ret = woal_associate_ssid_bssid(priv, wrq); break; case WOAL_WMM_QUEUE_STATUS: ret = woal_wmm_queue_status_ioctl(priv, wrq); break; case WOAL_WMM_TS_STATUS: ret = woal_wmm_ts_status_ioctl(priv, wrq); break; case WOAL_IP_ADDRESS: ret = woal_set_get_ip_addr(priv, wrq); break; case WOAL_TX_BF_CFG: ret = woal_tx_bf_cfg_ioctl(priv, wrq); break; default: ret = -EINVAL; break; } break; case WOAL_SETADDR_GETNONE: switch ((int)wrq->u.data.flags) { case WOAL_DEAUTH: ret = woal_deauth(priv, wrq); break; default: ret = -EINVAL; break; } break; case WOAL_SETNONE_GETTWELVE_CHAR: /* * We've not used IW_PRIV_TYPE_FIXED so sub-ioctl number is * in flags of iwreq structure, otherwise it will be in * mode member of iwreq structure. */ switch ((int)wrq->u.data.flags) { case WOAL_WPS_SESSION: ret = woal_wps_cfg_ioctl(priv, wrq); break; default: ret = -EINVAL; break; } break; case WOAL_SETNONE_GET_FOUR_INT: switch ((int)wrq->u.data.flags) { case WOAL_DATA_RATE: ret = woal_get_txrx_rate(priv, wrq); break; case WOAL_ESUPP_MODE: ret = woal_get_esupp_mode(priv, wrq); break; default: ret = -EINVAL; break; } break; case WOAL_SET_GET_64_INT: switch ((int)wrq->u.data.flags) { case WOAL_ECL_SYS_CLOCK: ret = woal_ecl_sys_clock(priv, wrq); break; } break; case WOAL_HOST_CMD: ret = woal_host_command(priv, wrq); break; case WOAL_ARP_FILTER: ret = woal_arp_filter(priv, wrq); break; case WOAL_SET_INTS_GET_CHARS: switch ((int)wrq->u.data.flags) { case WOAL_READ_EEPROM: ret = woal_read_eeprom(priv, wrq); break; } break; case WOAL_SET_GET_2K_BYTES: switch ((int)wrq->u.data.flags) { #ifdef SDIO case WOAL_CMD_53RDWR: ret = woal_cmd53rdwr_ioctl(priv, wrq); break; #endif case WOAL_SET_USER_SCAN: ret = woal_set_user_scan_ioctl(priv, wrq); break; case WOAL_GET_SCAN_TABLE: ret = woal_get_scan_table_ioctl(priv, wrq); break; case WOAL_SET_USER_SCAN_EXT: ret = woal_set_user_scan_ext_ioctl(priv, wrq); break; case WOAL_WMM_ADDTS: ret = woal_wmm_addts_req_ioctl(priv, wrq); break; case WOAL_WMM_DELTS: ret = woal_wmm_delts_req_ioctl(priv, wrq); break; case WOAL_WMM_QUEUE_CONFIG: ret = woal_wmm_queue_config_ioctl(priv, wrq); break; case WOAL_WMM_QUEUE_STATS: ret = woal_wmm_queue_stats_ioctl(priv, wrq); break; case WOAL_BYPASSED_PACKET: ret = woal_bypassed_packet_ioctl(priv, wrq); break; default: ret = -EINVAL; break; } break; #ifdef UAP_WEXT case WOAL_FROYO_START: break; case WOAL_FROYO_WL_FW_RELOAD: break; case WOAL_FROYO_STOP: if (IS_UAP_WEXT(priv->phandle->params.cfg80211_wext) && MLAN_STATUS_SUCCESS != woal_disconnect(priv, MOAL_IOCTL_WAIT, NULL, DEF_DEAUTH_REASON_CODE)) { ret = -EFAULT; } break; #endif default: ret = -EINVAL; break; } LEAVE(); return ret; } /** * @brief Get data rates * * @param priv A pointer to moal_private structure * @param wait_option Wait option * @param m_rates A pointer to moal_802_11_rates structure * * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, * otherwise fail */ mlan_status woal_get_data_rates(moal_private *priv, t_u8 wait_option, moal_802_11_rates *m_rates) { mlan_ds_rate *rate = NULL; mlan_ioctl_req *req = NULL; mlan_status status = MLAN_STATUS_SUCCESS; ENTER(); /* Allocate an IOCTL request buffer */ req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_rate)); if (req == NULL) { status = MLAN_STATUS_FAILURE; goto done; } /* Fill request buffer */ rate = (mlan_ds_rate *)req->pbuf; rate->sub_command = MLAN_OID_SUPPORTED_RATES; req->req_id = MLAN_IOCTL_RATE; req->action = MLAN_ACT_GET; /* Send IOCTL request to MLAN */ status = woal_request_ioctl(priv, req, wait_option); if (status == MLAN_STATUS_SUCCESS) { if (m_rates) m_rates->num_of_rates = woal_copy_rates( m_rates->rates, m_rates->num_of_rates, rate->param.rates, MLAN_SUPPORTED_RATES); } done: if (status != MLAN_STATUS_PENDING) kfree(req); LEAVE(); return status; } /** * @brief Get channel list * * @param priv A pointer to moal_private structure * @param wait_option Wait option * @param chan_list A pointer to mlan_chan_list structure * * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, * otherwise fail */ mlan_status woal_get_channel_list(moal_private *priv, t_u8 wait_option, mlan_chan_list *chan_list) { mlan_ds_bss *bss = NULL; mlan_ioctl_req *req = NULL; mlan_status status = MLAN_STATUS_SUCCESS; ENTER(); /* Allocate an IOCTL request buffer */ req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); if (req == NULL) { status = MLAN_STATUS_FAILURE; goto done; } /* Fill request buffer */ bss = (mlan_ds_bss *)req->pbuf; bss->sub_command = MLAN_OID_BSS_CHANNEL_LIST; req->req_id = MLAN_IOCTL_BSS; req->action = MLAN_ACT_GET; /* Send IOCTL request to MLAN */ status = woal_request_ioctl(priv, req, wait_option); if (status == MLAN_STATUS_SUCCESS) { if (chan_list) { moal_memcpy_ext(priv->phandle, chan_list, &bss->param.chanlist, sizeof(mlan_chan_list), sizeof(mlan_chan_list)); } } done: if (status != MLAN_STATUS_PENDING) kfree(req); LEAVE(); return status; } /** * @brief Handle get info resp * * @param priv Pointer to moal_private structure * @param info Pointer to mlan_ds_get_info structure * * @return N/A */ void woal_ioctl_get_info_resp(moal_private *priv, mlan_ds_get_info *info) { ENTER(); switch (info->sub_command) { case MLAN_OID_GET_STATS: priv->w_stats.discard.fragment = info->param.stats.fcs_error; priv->w_stats.discard.retries = info->param.stats.retry; priv->w_stats.discard.misc = info->param.stats.ack_failure; break; case MLAN_OID_GET_SIGNAL: if (info->param.signal.selector & BCN_RSSI_AVG_MASK) priv->w_stats.qual.level = info->param.signal.bcn_rssi_avg; if (info->param.signal.selector & BCN_NF_AVG_MASK) priv->w_stats.qual.noise = info->param.signal.bcn_nf_avg; break; default: break; } LEAVE(); } /** * @brief Handle get BSS resp * * @param priv Pointer to moal_private structure * @param bss Pointer to mlan_ds_bss structure * * @return N/A */ void woal_ioctl_get_bss_resp(moal_private *priv, mlan_ds_bss *bss) { t_u32 mode = 0; ENTER(); switch (bss->sub_command) { case MLAN_OID_BSS_MODE: if (bss->param.bss_mode == MLAN_BSS_MODE_INFRA) mode = IW_MODE_INFRA; else if (bss->param.bss_mode == MLAN_BSS_MODE_IBSS) mode = IW_MODE_ADHOC; else mode = IW_MODE_AUTO; priv->w_stats.status = mode; break; default: break; } LEAVE(); return; }