/** @file mlan_11n.c * * @brief This file contains functions for 11n handling. * * * Copyright 2008-2021 NXP * * This software file (the File) is distributed by NXP * under the terms of the GNU General Public License Version 2, June 1991 * (the License). You may use, redistribute and/or modify the File in * accordance with the terms and conditions of the License, a copy of which * is available by writing to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. * * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE * ARE EXPRESSLY DISCLAIMED. The License provides additional details about * this warranty disclaimer. * */ /******************************************************** Change log: 11/10/2008: initial version ********************************************************/ #include "mlan.h" #include "mlan_join.h" #include "mlan_util.h" #include "mlan_fw.h" #include "mlan_main.h" #include "mlan_wmm.h" #include "mlan_11n.h" #include "mlan_11ac.h" /******************************************************** Local Variables ********************************************************/ /******************************************************** Global Variables ********************************************************/ /******************************************************** Local Functions ********************************************************/ /** * * @brief set/get max tx buf size * * @param pmadapter A pointer to mlan_adapter structure * @param pioctl_req A pointer to ioctl request buffer * * @return MLAN_STATUS_SUCCESS --success, otherwise * fail */ static mlan_status wlan_11n_ioctl_max_tx_buf_size(pmlan_adapter pmadapter, pmlan_ioctl_req pioctl_req) { mlan_status ret = MLAN_STATUS_SUCCESS; mlan_ds_11n_cfg *cfg = MNULL; mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; ENTER(); cfg = (mlan_ds_11n_cfg *)pioctl_req->pbuf; if (pioctl_req->action == MLAN_ACT_SET) { if (cfg->param.tx_buf_size == 0xffff) { PRINTM(MIOCTL, "Send reconfigure tx buf to FW\n"); ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_RECONFIGURE_TX_BUFF, HostCmd_ACT_GEN_SET, 0, (t_void *)pioctl_req, &cfg->param.tx_buf_size); if (ret == MLAN_STATUS_SUCCESS) ret = MLAN_STATUS_PENDING; LEAVE(); return ret; } } cfg->param.tx_buf_size = (t_u32)pmadapter->max_tx_buf_size; pioctl_req->data_read_written = sizeof(t_u32) + MLAN_SUB_COMMAND_SIZE; LEAVE(); return ret; } /** * @brief Set/get htcapinfo configuration * * @param pmadapter A pointer to mlan_adapter structure * @param pioctl_req A pointer to ioctl request buffer * * @return MLAN_STATUS_SUCCESS --success, otherwise * fail */ static mlan_status wlan_11n_ioctl_htusrcfg(pmlan_adapter pmadapter, pmlan_ioctl_req pioctl_req) { mlan_status ret = MLAN_STATUS_SUCCESS; mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; mlan_ds_11n_cfg *cfg = MNULL; ENTER(); cfg = (mlan_ds_11n_cfg *)pioctl_req->pbuf; if (pioctl_req->action == MLAN_ACT_SET) { if (((cfg->param.htcap_cfg.htcap & ~IGN_HW_DEV_CAP) & pmpriv->adapter->hw_dot_11n_dev_cap) != (cfg->param.htcap_cfg.htcap & ~IGN_HW_DEV_CAP)) { pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; ret = MLAN_STATUS_FAILURE; } else { if (cfg->param.htcap_cfg.misc_cfg == BAND_SELECT_BG) { pmpriv->usr_dot_11n_dev_cap_bg = cfg->param.htcap_cfg.htcap; PRINTM(MINFO, "Set: UsrDot11nCap for 2.4GHz 0x%x\n", pmpriv->usr_dot_11n_dev_cap_bg); } if (cfg->param.htcap_cfg.misc_cfg == BAND_SELECT_A) { pmpriv->usr_dot_11n_dev_cap_a = cfg->param.htcap_cfg.htcap; PRINTM(MINFO, "Set: UsrDot11nCap for 5GHz 0x%x\n", pmpriv->usr_dot_11n_dev_cap_a); } if (cfg->param.htcap_cfg.misc_cfg == BAND_SELECT_BOTH) { pmpriv->usr_dot_11n_dev_cap_bg = cfg->param.htcap_cfg.htcap; pmpriv->usr_dot_11n_dev_cap_a = cfg->param.htcap_cfg.htcap; PRINTM(MINFO, "Set: UsrDot11nCap for 2.4GHz and 5GHz 0x%x\n", cfg->param.htcap_cfg.htcap); } } } else { /* Hardware 11N device capability required */ if (cfg->param.htcap_cfg.hw_cap_req) cfg->param.htcap_cfg.htcap = pmadapter->hw_dot_11n_dev_cap; else { if (cfg->param.htcap_cfg.misc_cfg == BAND_SELECT_BG) { cfg->param.htcap_cfg.htcap = pmpriv->usr_dot_11n_dev_cap_bg; PRINTM(MINFO, "Get: UsrDot11nCap for 2.4GHz 0x%x\n", cfg->param.htcap_cfg.htcap); } if (cfg->param.htcap_cfg.misc_cfg == BAND_SELECT_A) { cfg->param.htcap_cfg.htcap = pmpriv->usr_dot_11n_dev_cap_a; PRINTM(MINFO, "Get: UsrDot11nCap for 5GHz 0x%x\n", cfg->param.htcap_cfg.htcap); } } } LEAVE(); return ret; } /** * @brief Enable/Disable AMSDU AGGR CTRL * * @param pmadapter A pointer to mlan_adapter structure * @param pioctl_req A pointer to ioctl request buffer * * @return MLAN_STATUS_PENDING --success, otherwise fail */ static mlan_status wlan_11n_ioctl_amsdu_aggr_ctrl(pmlan_adapter pmadapter, pmlan_ioctl_req pioctl_req) { mlan_status ret = MLAN_STATUS_SUCCESS; mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; mlan_ds_11n_cfg *cfg = MNULL; t_u16 cmd_action = 0; ENTER(); cfg = (mlan_ds_11n_cfg *)pioctl_req->pbuf; if (pioctl_req->action == MLAN_ACT_SET) cmd_action = HostCmd_ACT_GEN_SET; else cmd_action = HostCmd_ACT_GEN_GET; /* Send request to firmware */ ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_AMSDU_AGGR_CTRL, cmd_action, 0, (t_void *)pioctl_req, (t_void *)&cfg->param.amsdu_aggr_ctrl); if (ret == MLAN_STATUS_SUCCESS) ret = MLAN_STATUS_PENDING; LEAVE(); return ret; } /** * @brief Set/get 11n configuration * * @param pmadapter A pointer to mlan_adapter structure * @param pioctl_req A pointer to ioctl request buffer * * @return MLAN_STATUS_PENDING --success, otherwise fail */ static mlan_status wlan_11n_ioctl_httxcfg(pmlan_adapter pmadapter, pmlan_ioctl_req pioctl_req) { mlan_status ret = MLAN_STATUS_SUCCESS; mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; mlan_ds_11n_cfg *cfg = MNULL; t_u16 cmd_action = 0; ENTER(); cfg = (mlan_ds_11n_cfg *)pioctl_req->pbuf; if (pioctl_req->action == MLAN_ACT_SET) cmd_action = HostCmd_ACT_GEN_SET; else cmd_action = HostCmd_ACT_GEN_GET; /* Send request to firmware */ ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_11N_CFG, cmd_action, 0, (t_void *)pioctl_req, (t_void *)&cfg->param.tx_cfg); if (ret == MLAN_STATUS_SUCCESS) ret = MLAN_STATUS_PENDING; LEAVE(); return ret; } /** * @brief Set/get TX beamforming capabilities * * @param pmadapter A pointer to mlan_adapter structure * @param pioctl_req A pointer to ioctl request buffer * * @return MLAN_STATUS_SUCCESS --success */ static mlan_status wlan_11n_ioctl_tx_bf_cap(pmlan_adapter pmadapter, pmlan_ioctl_req pioctl_req) { mlan_status ret = MLAN_STATUS_SUCCESS; mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; mlan_ds_11n_cfg *cfg = MNULL; ENTER(); cfg = (mlan_ds_11n_cfg *)pioctl_req->pbuf; if (pioctl_req->action == MLAN_ACT_SET) pmpriv->tx_bf_cap = cfg->param.tx_bf_cap; else cfg->param.tx_bf_cap = pmpriv->tx_bf_cap; LEAVE(); return ret; } /** * @brief Set/get TX beamforming configurations * * @param pmadapter A pointer to mlan_adapter structure * @param pioctl_req A pointer to ioctl request buffer * * @return MLAN_STATUS_PENDING --success, otherwise fail */ static mlan_status wlan_11n_ioctl_tx_bf_cfg(pmlan_adapter pmadapter, pmlan_ioctl_req pioctl_req) { mlan_status ret = MLAN_STATUS_SUCCESS; mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; mlan_ds_11n_cfg *cfg = MNULL; t_u16 cmd_action = 0; ENTER(); cfg = (mlan_ds_11n_cfg *)pioctl_req->pbuf; if (pioctl_req->action == MLAN_ACT_SET) cmd_action = HostCmd_ACT_GEN_SET; else cmd_action = HostCmd_ACT_GEN_GET; /* Send request to firmware */ ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_TX_BF_CFG, cmd_action, 0, (t_void *)pioctl_req, (t_void *)&cfg->param.tx_bf); if (ret == MLAN_STATUS_SUCCESS) ret = MLAN_STATUS_PENDING; LEAVE(); return ret; } /** * @brief Set/get HT stream configurations * * @param pmadapter A pointer to mlan_adapter structure * @param pioctl_req A pointer to ioctl request buffer * * @return MLAN_STATUS_SUCCESS --success, otherwise fail */ static mlan_status wlan_11n_ioctl_stream_cfg(pmlan_adapter pmadapter, pmlan_ioctl_req pioctl_req) { mlan_status ret = MLAN_STATUS_SUCCESS; mlan_ds_11n_cfg *cfg = MNULL; mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; ENTER(); cfg = (mlan_ds_11n_cfg *)pioctl_req->pbuf; if (pioctl_req->action == MLAN_ACT_GET) { cfg->param.stream_cfg = pmpriv->usr_dev_mcs_support; } else if (pioctl_req->action == MLAN_ACT_SET) { switch (cfg->param.stream_cfg) { case HT_STREAM_MODE_2X2: if (pmadapter->hw_dev_mcs_support == HT_STREAM_MODE_1X1) { PRINTM(MERROR, "HW does not support this mode\n"); ret = MLAN_STATUS_FAILURE; } else pmpriv->usr_dev_mcs_support = cfg->param.stream_cfg; break; case HT_STREAM_MODE_1X1: pmpriv->usr_dev_mcs_support = cfg->param.stream_cfg; break; default: PRINTM(MERROR, "Invalid stream mode\n"); pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; ret = MLAN_STATUS_FAILURE; break; } } LEAVE(); return ret; } /** * @brief Set/get control to coex RX window size configuration * * @param pmadapter A pointer to mlan_adapter structure * @param pioctl_req A pointer to ioctl request buffer * * @return MLAN_STATUS_SUCCESS --success, otherwise fail */ static mlan_status wlan_11n_ioctl_coex_rx_winsize(pmlan_adapter pmadapter, pmlan_ioctl_req pioctl_req) { mlan_status ret = MLAN_STATUS_SUCCESS; mlan_ds_11n_cfg *cfg = MNULL; ENTER(); cfg = (mlan_ds_11n_cfg *)pioctl_req->pbuf; if (pioctl_req->action == MLAN_ACT_GET) cfg->param.coex_rx_winsize = pmadapter->coex_rx_winsize; else if (pioctl_req->action == MLAN_ACT_SET) pmadapter->coex_rx_winsize = (t_u8)cfg->param.coex_rx_winsize; LEAVE(); return ret; } /** * @brief This function will send delba request to * the peer in the TxBAStreamTbl * * @param priv A pointer to mlan_private * @param ra MAC Address to send DELBA * * @return N/A */ void wlan_11n_send_delba_to_peer(mlan_private *priv, t_u8 *ra) { TxBAStreamTbl *ptx_tbl; ENTER(); wlan_request_ralist_lock(priv); ptx_tbl = (TxBAStreamTbl *)util_peek_list(priv->adapter->pmoal_handle, &priv->tx_ba_stream_tbl_ptr, MNULL, MNULL); if (!ptx_tbl) { wlan_release_ralist_lock(priv); LEAVE(); return; } while (ptx_tbl != (TxBAStreamTbl *)&priv->tx_ba_stream_tbl_ptr) { if (!memcmp(priv->adapter, ptx_tbl->ra, ra, MLAN_MAC_ADDR_LENGTH)) { PRINTM(MIOCTL, "Tx:Send delba to tid=%d, " MACSTR "\n", ptx_tbl->tid, MAC2STR(ptx_tbl->ra)); ptx_tbl->ba_status = BA_STREAM_SENT_DELBA; wlan_send_delba(priv, MNULL, ptx_tbl->tid, ptx_tbl->ra, 1); } ptx_tbl = ptx_tbl->pnext; } wlan_release_ralist_lock(priv); /* Signal MOAL to trigger mlan_main_process */ wlan_recv_event(priv, MLAN_EVENT_ID_DRV_DEFER_HANDLING, MNULL); LEAVE(); return; } /** * @brief Set/Get control to TX AMPDU configuration on infra link * * @param pmadapter A pointer to mlan_adapter structure * @param pioctl_req A pointer to ioctl request buffer * * @return MLAN_STATUS_SUCCESS --success, otherwise fail */ static mlan_status wlan_11n_ioctl_txaggrctrl(pmlan_adapter pmadapter, pmlan_ioctl_req pioctl_req) { mlan_status ret = MLAN_STATUS_SUCCESS; mlan_ds_11n_cfg *cfg = MNULL; mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; ENTER(); cfg = (mlan_ds_11n_cfg *)pioctl_req->pbuf; if (pioctl_req->action == MLAN_ACT_GET) cfg->param.txaggrctrl = pmpriv->txaggrctrl; else if (pioctl_req->action == MLAN_ACT_SET) pmpriv->txaggrctrl = (t_u8)cfg->param.txaggrctrl; if (pmpriv->media_connected == MTRUE) { if (pioctl_req->action == MLAN_ACT_SET && !pmpriv->txaggrctrl && pmpriv->adapter->tdls_status != TDLS_NOT_SETUP) wlan_11n_send_delba_to_peer( pmpriv, pmpriv->curr_bss_params.bss_descriptor .mac_address); } LEAVE(); return ret; } /** * @brief This function will resend addba request to all * the peer in the TxBAStreamTbl * * @param priv A pointer to mlan_private * * @return N/A */ static void wlan_11n_update_addba_request(mlan_private *priv) { TxBAStreamTbl *ptx_tbl; ENTER(); wlan_request_ralist_lock(priv); ptx_tbl = (TxBAStreamTbl *)util_peek_list(priv->adapter->pmoal_handle, &priv->tx_ba_stream_tbl_ptr, MNULL, MNULL); if (!ptx_tbl) { wlan_release_ralist_lock(priv); LEAVE(); return; } while (ptx_tbl != (TxBAStreamTbl *)&priv->tx_ba_stream_tbl_ptr) { ptx_tbl->ba_status = BA_STREAM_SETUP_SENT_ADDBA; wlan_send_addba(priv, ptx_tbl->tid, ptx_tbl->ra); ptx_tbl = ptx_tbl->pnext; } wlan_release_ralist_lock(priv); /* Signal MOAL to trigger mlan_main_process */ wlan_recv_event(priv, MLAN_EVENT_ID_DRV_DEFER_HANDLING, MNULL); LEAVE(); return; } /** * @brief Set/get addba parameter * * @param pmadapter A pointer to mlan_adapter structure * @param pioctl_req A pointer to ioctl request buffer * * @return MLAN_STATUS_SUCCESS --success */ static mlan_status wlan_11n_ioctl_addba_param(pmlan_adapter pmadapter, pmlan_ioctl_req pioctl_req) { mlan_status ret = MLAN_STATUS_SUCCESS; mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; mlan_ds_11n_cfg *cfg = MNULL; t_u32 timeout; ENTER(); cfg = (mlan_ds_11n_cfg *)pioctl_req->pbuf; if (pioctl_req->action == MLAN_ACT_GET) { cfg->param.addba_param.timeout = pmpriv->add_ba_param.timeout; cfg->param.addba_param.txwinsize = pmpriv->add_ba_param.tx_win_size; cfg->param.addba_param.rxwinsize = pmpriv->add_ba_param.rx_win_size; cfg->param.addba_param.txamsdu = pmpriv->add_ba_param.tx_amsdu; cfg->param.addba_param.rxamsdu = pmpriv->add_ba_param.rx_amsdu; } else { timeout = pmpriv->add_ba_param.timeout; pmpriv->add_ba_param.timeout = cfg->param.addba_param.timeout; pmpriv->add_ba_param.tx_win_size = cfg->param.addba_param.txwinsize; pmpriv->add_ba_param.rx_win_size = cfg->param.addba_param.rxwinsize; pmpriv->user_rxwinsize = pmpriv->add_ba_param.rx_win_size; pmpriv->add_ba_param.tx_amsdu = cfg->param.addba_param.txamsdu; pmpriv->add_ba_param.rx_amsdu = cfg->param.addba_param.rxamsdu; if (timeout != pmpriv->add_ba_param.timeout) wlan_11n_update_addba_request(pmpriv); } LEAVE(); return ret; } /** * @brief This function send delba to specific tid * * @param priv A pointer to mlan_priv * @param tid tid * @return N/A */ void wlan_11n_delba(mlan_private *priv, int tid) { RxReorderTbl *rx_reor_tbl_ptr; ENTER(); rx_reor_tbl_ptr = (RxReorderTbl *)util_peek_list( priv->adapter->pmoal_handle, &priv->rx_reorder_tbl_ptr, priv->adapter->callbacks.moal_spin_lock, priv->adapter->callbacks.moal_spin_unlock); if (!rx_reor_tbl_ptr) { LEAVE(); return; } while (rx_reor_tbl_ptr != (RxReorderTbl *)&priv->rx_reorder_tbl_ptr) { if (rx_reor_tbl_ptr->tid == tid) { PRINTM(MIOCTL, "Send delba to tid=%d, " MACSTR "\n", tid, MAC2STR(rx_reor_tbl_ptr->ta)); wlan_send_delba(priv, MNULL, tid, rx_reor_tbl_ptr->ta, 0); LEAVE(); return; } rx_reor_tbl_ptr = rx_reor_tbl_ptr->pnext; } LEAVE(); return; } /** * @brief Set/get addba reject set * * @param pmadapter A pointer to mlan_adapter structure * @param pioctl_req A pointer to ioctl request buffer * * @return MLAN_STATUS_SUCCESS --success, otherwise fail */ static mlan_status wlan_11n_ioctl_addba_reject(pmlan_adapter pmadapter, pmlan_ioctl_req pioctl_req) { int i = 0; mlan_status ret = MLAN_STATUS_SUCCESS; mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; mlan_ds_11n_cfg *cfg = MNULL; ENTER(); cfg = (mlan_ds_11n_cfg *)pioctl_req->pbuf; if (pioctl_req->action == MLAN_ACT_GET) { PRINTM(MINFO, "Get Addba reject\n"); memcpy_ext(pmadapter, cfg->param.addba_reject, pmpriv->addba_reject, MAX_NUM_TID, MAX_NUM_TID); } else { for (i = 0; i < MAX_NUM_TID; i++) { /* For AMPDU */ if (cfg->param.addba_reject[i] > ADDBA_RSP_STATUS_REJECT) { pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; ret = MLAN_STATUS_FAILURE; break; } pmpriv->addba_reject[i] = cfg->param.addba_reject[i]; } if (pmpriv->media_connected == MTRUE) { for (i = 0; i < MAX_NUM_TID; i++) { if (cfg->param.addba_reject[i] == ADDBA_RSP_STATUS_REJECT) { PRINTM(MIOCTL, "Receive addba reject: tid=%d\n", i); wlan_11n_delba(pmpriv, i); } } wlan_recv_event(pmpriv, MLAN_EVENT_ID_DRV_DEFER_HANDLING, MNULL); } } LEAVE(); return ret; } /** * @brief Set/get ibss ampdu param * * @param pmadapter A pointer to mlan_adapter structure * @param pioctl_req A pointer to ioctl request buffer * * @return MLAN_STATUS_SUCCESS --success, otherwise fail */ static mlan_status wlan_11n_ioctl_ibss_ampdu_param(pmlan_adapter pmadapter, pmlan_ioctl_req pioctl_req) { int i = 0; mlan_status ret = MLAN_STATUS_SUCCESS; mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; mlan_ds_11n_cfg *cfg = MNULL; ENTER(); cfg = (mlan_ds_11n_cfg *)pioctl_req->pbuf; if (pioctl_req->action == MLAN_ACT_GET) { PRINTM(MINFO, "Get IBSS AMPDU param\n"); for (i = 0; i < MAX_NUM_TID; i++) { cfg->param.ibss_ampdu.ampdu[i] = pmpriv->ibss_ampdu[i]; cfg->param.ibss_ampdu.addba_reject[i] = pmpriv->ibss_addba_reject[i]; } } else { for (i = 0; i < MAX_NUM_TID; i++) { /* For AMPDU RX*/ if (cfg->param.ibss_ampdu.addba_reject[i] > ADDBA_RSP_STATUS_REJECT) { pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; ret = MLAN_STATUS_FAILURE; break; } pmpriv->ibss_addba_reject[i] = cfg->param.ibss_ampdu.addba_reject[i]; /* For AMPDU TX*/ if ((cfg->param.ibss_ampdu.ampdu[i] > HIGH_PRIO_TID) && (cfg->param.ibss_ampdu.ampdu[i] != BA_STREAM_NOT_ALLOWED)) { pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; ret = MLAN_STATUS_FAILURE; break; } pmpriv->ibss_ampdu[i] = cfg->param.ibss_ampdu.ampdu[i]; } PRINTM(MMSG, "IBSS addba reject: %d %d %d %d %d %d %d %d\n", pmpriv->ibss_addba_reject[0], pmpriv->ibss_addba_reject[1], pmpriv->ibss_addba_reject[2], pmpriv->ibss_addba_reject[3], pmpriv->ibss_addba_reject[4], pmpriv->ibss_addba_reject[5], pmpriv->ibss_addba_reject[6], pmpriv->ibss_addba_reject[7]); PRINTM(MMSG, "IBSS ampdu %d %d %d %d %d %d %d %d\n", pmpriv->ibss_ampdu[0], pmpriv->ibss_ampdu[1], pmpriv->ibss_ampdu[2], pmpriv->ibss_ampdu[3], pmpriv->ibss_ampdu[4], pmpriv->ibss_ampdu[5], pmpriv->ibss_ampdu[6], pmpriv->ibss_ampdu[7]); } LEAVE(); return ret; } /** * @brief Set/Get Minimum BA Threshold * * @param pmadapter A pointer to mlan_adapter structure * @param pioctl_req A pointer to ioctl request buffer * * @return MLAN_STATUS_SUCCESS --success */ static mlan_status wlan_11n_ioctl_min_ba_threshold_cfg(pmlan_adapter pmadapter, pmlan_ioctl_req pioctl_req) { mlan_status ret = MLAN_STATUS_SUCCESS; mlan_ds_11n_cfg *cfg = MNULL; ENTER(); cfg = (mlan_ds_11n_cfg *)pioctl_req->pbuf; if (pioctl_req->action == MLAN_ACT_GET) cfg->param.min_ba_threshold = pmadapter->min_ba_threshold; else pmadapter->min_ba_threshold = cfg->param.min_ba_threshold; pioctl_req->data_read_written = sizeof(t_u8) + MLAN_SUB_COMMAND_SIZE; LEAVE(); return ret; } /** * @brief This function will send DELBA to entries in the priv's * Tx BA stream table * * @param priv A pointer to mlan_private * @param pioctl_req A pointer to ioctl request buffer * @param tid TID * @param peer_address A pointer to peer address * @param last_tx_ba_to_delete A pointer to the last entry in TxBAStreamTbl * * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_PENDING */ static mlan_status wlan_send_delba_to_entry_in_txbastream_tbl( pmlan_private priv, pmlan_ioctl_req pioctl_req, t_u8 tid, t_u8 *peer_address, TxBAStreamTbl *last_tx_ba_to_delete) { pmlan_adapter pmadapter = priv->adapter; TxBAStreamTbl *tx_ba_stream_tbl_ptr; t_u8 zero_mac[MLAN_MAC_ADDR_LENGTH] = {0}; mlan_status ret = MLAN_STATUS_SUCCESS; ENTER(); wlan_request_ralist_lock(priv); tx_ba_stream_tbl_ptr = (TxBAStreamTbl *)util_peek_list(pmadapter->pmoal_handle, &priv->tx_ba_stream_tbl_ptr, MNULL, MNULL); if (!tx_ba_stream_tbl_ptr) { wlan_release_ralist_lock(priv); LEAVE(); return ret; } while (tx_ba_stream_tbl_ptr != (TxBAStreamTbl *)&priv->tx_ba_stream_tbl_ptr) { if (tx_ba_stream_tbl_ptr->ba_status == BA_STREAM_SETUP_COMPLETE) { if (((tid == DELBA_ALL_TIDS) || (tid == tx_ba_stream_tbl_ptr->tid)) && (!memcmp(pmadapter, peer_address, zero_mac, MLAN_MAC_ADDR_LENGTH) || !memcmp(pmadapter, peer_address, tx_ba_stream_tbl_ptr->ra, MLAN_MAC_ADDR_LENGTH))) { tx_ba_stream_tbl_ptr->ba_status = BA_STREAM_SENT_DELBA; if (last_tx_ba_to_delete && (tx_ba_stream_tbl_ptr == last_tx_ba_to_delete)) ret = wlan_send_delba( priv, pioctl_req, tx_ba_stream_tbl_ptr->tid, tx_ba_stream_tbl_ptr->ra, 1); else ret = wlan_send_delba( priv, MNULL, tx_ba_stream_tbl_ptr->tid, tx_ba_stream_tbl_ptr->ra, 1); } } tx_ba_stream_tbl_ptr = tx_ba_stream_tbl_ptr->pnext; } wlan_release_ralist_lock(priv); LEAVE(); return ret; } /** * @brief This function will send DELBA to entries in the priv's * rx reordering table * * @param priv A pointer to mlan_private * @param pioctl_req A pointer to ioctl request buffer * @param tid TID * @param peer_address A pointer to peer address * @param last_rx_ba_to_delete A pointer to the last entry in RxReorderTbl * * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_PENDING */ static mlan_status wlan_send_delba_to_entry_in_reorder_tbl( pmlan_private priv, pmlan_ioctl_req pioctl_req, t_u8 tid, t_u8 *peer_address, RxReorderTbl *last_rx_ba_to_delete) { pmlan_adapter pmadapter = priv->adapter; RxReorderTbl *rx_reor_tbl_ptr; t_u8 zero_mac[MLAN_MAC_ADDR_LENGTH] = {0}; mlan_status ret = MLAN_STATUS_SUCCESS; ENTER(); rx_reor_tbl_ptr = (RxReorderTbl *)util_peek_list( pmadapter->pmoal_handle, &priv->rx_reorder_tbl_ptr, pmadapter->callbacks.moal_spin_lock, pmadapter->callbacks.moal_spin_unlock); if (!rx_reor_tbl_ptr) { LEAVE(); return ret; } while (rx_reor_tbl_ptr != (RxReorderTbl *)&priv->rx_reorder_tbl_ptr) { if (rx_reor_tbl_ptr->ba_status == BA_STREAM_SETUP_COMPLETE) { if (((tid == DELBA_ALL_TIDS) || (tid == rx_reor_tbl_ptr->tid)) && (!memcmp(pmadapter, peer_address, zero_mac, MLAN_MAC_ADDR_LENGTH) || !memcmp(pmadapter, peer_address, rx_reor_tbl_ptr->ta, MLAN_MAC_ADDR_LENGTH))) { if (last_rx_ba_to_delete && (rx_reor_tbl_ptr == last_rx_ba_to_delete)) ret = wlan_send_delba( priv, pioctl_req, rx_reor_tbl_ptr->tid, rx_reor_tbl_ptr->ta, 0); else ret = wlan_send_delba( priv, MNULL, rx_reor_tbl_ptr->tid, rx_reor_tbl_ptr->ta, 0); } } rx_reor_tbl_ptr = rx_reor_tbl_ptr->pnext; } LEAVE(); return ret; } /** * @brief IOCTL to delete BA * * @param pmadapter A pointer to mlan_adapter structure * @param pioctl_req A pointer to ioctl request buffer * * @return MLAN_STATUS_SUCCESS --success, otherwise fail */ static mlan_status wlan_11n_ioctl_delba(pmlan_adapter pmadapter, pmlan_ioctl_req pioctl_req) { mlan_status ret = MLAN_STATUS_SUCCESS; mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; mlan_ds_11n_cfg *cfg = MNULL; TxBAStreamTbl *tx_ba_stream_tbl_ptr, *last_tx_ba_to_delete = MNULL; RxReorderTbl *rx_reor_tbl_ptr, *last_rx_ba_to_delete = MNULL; t_u8 zero_mac[MLAN_MAC_ADDR_LENGTH] = {0}; t_u8 tid, *peer_address; ENTER(); cfg = (mlan_ds_11n_cfg *)pioctl_req->pbuf; tid = cfg->param.del_ba.tid; peer_address = cfg->param.del_ba.peer_mac_addr; PRINTM(MINFO, "DelBA: direction %d, TID %d, peer address " MACSTR "\n", cfg->param.del_ba.direction, tid, MAC2STR(peer_address)); if (cfg->param.del_ba.direction & DELBA_RX) { rx_reor_tbl_ptr = (RxReorderTbl *)util_peek_list( pmadapter->pmoal_handle, &pmpriv->rx_reorder_tbl_ptr, pmadapter->callbacks.moal_spin_lock, pmadapter->callbacks.moal_spin_unlock); if (rx_reor_tbl_ptr) { while (rx_reor_tbl_ptr != (RxReorderTbl *)&pmpriv->rx_reorder_tbl_ptr) { if (rx_reor_tbl_ptr->ba_status == BA_STREAM_SETUP_COMPLETE) { if (((tid == DELBA_ALL_TIDS) || (tid == rx_reor_tbl_ptr->tid)) && (!memcmp(pmadapter, peer_address, zero_mac, MLAN_MAC_ADDR_LENGTH) || !memcmp(pmadapter, peer_address, rx_reor_tbl_ptr->ta, MLAN_MAC_ADDR_LENGTH))) { /* Found RX BA to delete */ last_rx_ba_to_delete = rx_reor_tbl_ptr; } } rx_reor_tbl_ptr = rx_reor_tbl_ptr->pnext; } } } if ((last_rx_ba_to_delete == MNULL) && (cfg->param.del_ba.direction & DELBA_TX)) { wlan_request_ralist_lock(pmpriv); tx_ba_stream_tbl_ptr = (TxBAStreamTbl *)util_peek_list( pmadapter->pmoal_handle, &pmpriv->tx_ba_stream_tbl_ptr, MNULL, MNULL); if (tx_ba_stream_tbl_ptr) { while (tx_ba_stream_tbl_ptr != (TxBAStreamTbl *)&pmpriv->tx_ba_stream_tbl_ptr) { if (tx_ba_stream_tbl_ptr->ba_status == BA_STREAM_SETUP_COMPLETE) { if (((tid == DELBA_ALL_TIDS) || (tid == tx_ba_stream_tbl_ptr->tid)) && (!memcmp(pmadapter, peer_address, zero_mac, MLAN_MAC_ADDR_LENGTH) || !memcmp(pmadapter, peer_address, tx_ba_stream_tbl_ptr->ra, MLAN_MAC_ADDR_LENGTH))) { /* Found TX BA to delete */ last_tx_ba_to_delete = tx_ba_stream_tbl_ptr; } } tx_ba_stream_tbl_ptr = tx_ba_stream_tbl_ptr->pnext; } } wlan_release_ralist_lock(pmpriv); } if (cfg->param.del_ba.direction & DELBA_TX) { if (last_rx_ba_to_delete) ret = wlan_send_delba_to_entry_in_txbastream_tbl( pmpriv, MNULL, tid, peer_address, MNULL); else ret = wlan_send_delba_to_entry_in_txbastream_tbl( pmpriv, pioctl_req, tid, peer_address, last_tx_ba_to_delete); } if (last_rx_ba_to_delete) { ret = wlan_send_delba_to_entry_in_reorder_tbl( pmpriv, pioctl_req, tid, peer_address, last_rx_ba_to_delete); } LEAVE(); return ret; } /** * @brief IOCTL to reject addba req * * @param pmadapter A pointer to mlan_adapter structure * @param pioctl_req A pointer to ioctl request buffer * * @return MLAN_STATUS_SUCCESS --success, otherwise fail */ static mlan_status wlan_11n_ioctl_rejectaddbareq(pmlan_adapter pmadapter, pmlan_ioctl_req pioctl_req) { mlan_status ret = MLAN_STATUS_SUCCESS; mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; mlan_ds_11n_cfg *cfg = MNULL; t_u16 cmd_action = 0; ENTER(); cfg = (mlan_ds_11n_cfg *)pioctl_req->pbuf; if (pioctl_req->action == MLAN_ACT_SET) cmd_action = HostCmd_ACT_GEN_SET; else cmd_action = HostCmd_ACT_GEN_GET; /* Send command to firmware */ ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_REJECT_ADDBA_REQ, cmd_action, 0, (t_void *)pioctl_req, &cfg->param.reject_addba_req); if (ret == MLAN_STATUS_SUCCESS) ret = MLAN_STATUS_PENDING; LEAVE(); return ret; } /** * @brief This function will send DELBA to entries in the priv's * Tx BA stream table * * @param priv A pointer to mlan_private * @param tid TID * * @return N/A */ static void wlan_send_delba_txbastream_tbl(pmlan_private priv, t_u8 tid) { pmlan_adapter pmadapter = priv->adapter; TxBAStreamTbl *tx_ba_stream_tbl_ptr; ENTER(); wlan_request_ralist_lock(priv); tx_ba_stream_tbl_ptr = (TxBAStreamTbl *)util_peek_list(pmadapter->pmoal_handle, &priv->tx_ba_stream_tbl_ptr, MNULL, MNULL); if (!tx_ba_stream_tbl_ptr) { wlan_release_ralist_lock(priv); LEAVE(); return; } while (tx_ba_stream_tbl_ptr != (TxBAStreamTbl *)&priv->tx_ba_stream_tbl_ptr) { if (tx_ba_stream_tbl_ptr->ba_status == BA_STREAM_SETUP_COMPLETE) { if (tid == tx_ba_stream_tbl_ptr->tid) { PRINTM(MIOCTL, "Tx:Send delba to tid=%d, " MACSTR "\n", tid, MAC2STR(tx_ba_stream_tbl_ptr->ra)); tx_ba_stream_tbl_ptr->ba_status = BA_STREAM_SENT_DELBA; wlan_release_ralist_lock(priv); wlan_send_delba(priv, MNULL, tx_ba_stream_tbl_ptr->tid, tx_ba_stream_tbl_ptr->ra, 1); LEAVE(); return; } } tx_ba_stream_tbl_ptr = tx_ba_stream_tbl_ptr->pnext; } wlan_release_ralist_lock(priv); LEAVE(); return; } /** * @brief update station list for the new aggr_prio_tbl setting * * @param priv A pointer to mlan_private structure * * * @return N/A */ static void wlan_update_all_stations_ampdu(mlan_private *priv) { sta_node *sta_ptr; mlan_adapter *pmadapter = priv->adapter; int i = 0; ENTER(); pmadapter->callbacks.moal_spin_lock(pmadapter->pmoal_handle, priv->wmm.ra_list_spinlock); sta_ptr = (sta_node *)util_peek_list(pmadapter->pmoal_handle, &priv->sta_list, MNULL, MNULL); if (!sta_ptr) { pmadapter->callbacks.moal_spin_unlock( pmadapter->pmoal_handle, priv->wmm.ra_list_spinlock); LEAVE(); return; } while (sta_ptr != (sta_node *)&priv->sta_list) { for (i = 0; i < MAX_NUM_TID; i++) { if (sta_ptr->is_11n_enabled || sta_ptr->is_11ax_enabled) sta_ptr->ampdu_sta[i] = priv->aggr_prio_tbl[i].ampdu_user; } sta_ptr = sta_ptr->pnext; } pmadapter->callbacks.moal_spin_unlock(pmadapter->pmoal_handle, priv->wmm.ra_list_spinlock); LEAVE(); return; } /** * @brief Set/get aggr_prio_tbl * * @param pmadapter A pointer to mlan_adapter structure * @param pioctl_req A pointer to ioctl request buffer * * @return MLAN_STATUS_SUCCESS --success, otherwise fail */ static mlan_status wlan_11n_ioctl_aggr_prio_tbl(pmlan_adapter pmadapter, pmlan_ioctl_req pioctl_req) { int i = 0; mlan_status ret = MLAN_STATUS_SUCCESS; mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; mlan_ds_11n_cfg *cfg = MNULL; ENTER(); cfg = (mlan_ds_11n_cfg *)pioctl_req->pbuf; if (pioctl_req->action == MLAN_ACT_GET) { for (i = 0; i < MAX_NUM_TID; i++) { cfg->param.aggr_prio_tbl.ampdu[i] = pmpriv->aggr_prio_tbl[i].ampdu_user; cfg->param.aggr_prio_tbl.amsdu[i] = pmpriv->aggr_prio_tbl[i].amsdu; } } else { for (i = 0; i < MAX_NUM_TID; i++) { /* For AMPDU */ if ((cfg->param.aggr_prio_tbl.ampdu[i] > HIGH_PRIO_TID) && (cfg->param.aggr_prio_tbl.ampdu[i] != BA_STREAM_NOT_ALLOWED)) { pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; ret = MLAN_STATUS_FAILURE; break; } pmpriv->aggr_prio_tbl[i].ampdu_ap = pmpriv->aggr_prio_tbl[i].ampdu_user = cfg->param.aggr_prio_tbl.ampdu[i]; /* For AMSDU */ if ((cfg->param.aggr_prio_tbl.amsdu[i] > HIGH_PRIO_TID && cfg->param.aggr_prio_tbl.amsdu[i] != BA_STREAM_NOT_ALLOWED)) { pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; ret = MLAN_STATUS_FAILURE; break; } else { pmpriv->aggr_prio_tbl[i].amsdu = cfg->param.aggr_prio_tbl.amsdu[i]; } } if (pmpriv->media_connected == MTRUE) { for (i = 0; i < MAX_NUM_TID; i++) { if (cfg->param.aggr_prio_tbl.ampdu[i] == BA_STREAM_NOT_ALLOWED) { PRINTM(MIOCTL, "Receive aggrpriotbl: BA not allowed tid=%d\n", i); wlan_send_delba_txbastream_tbl(pmpriv, i); } } wlan_update_all_stations_ampdu(pmpriv); wlan_recv_event(pmpriv, MLAN_EVENT_ID_DRV_DEFER_HANDLING, MNULL); } } LEAVE(); return ret; } /** * @brief This function update all the tx_win_size * * @param pmadapter A pointer to mlan_adapter * * * @return N/A */ void wlan_update_ampdu_txwinsize(pmlan_adapter pmadapter) { t_u8 i; t_u32 tx_win_size = 0; pmlan_private priv = MNULL; ENTER(); for (i = 0; i < pmadapter->priv_num; i++) { if (pmadapter->priv[i]) { priv = pmadapter->priv[i]; tx_win_size = priv->add_ba_param.tx_win_size; #ifdef STA_SUPPORT if (priv->bss_type == MLAN_BSS_TYPE_STA) priv->add_ba_param.tx_win_size = MLAN_STA_AMPDU_DEF_TXWINSIZE; #endif #ifdef WIFI_DIRECT_SUPPORT if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT) priv->add_ba_param.tx_win_size = MLAN_WFD_AMPDU_DEF_TXRXWINSIZE; #endif #ifdef UAP_SUPPORT if (priv->bss_type == MLAN_BSS_TYPE_UAP) priv->add_ba_param.tx_win_size = MLAN_UAP_AMPDU_DEF_TXWINSIZE; #endif if (pmadapter->coex_win_size && pmadapter->coex_tx_win_size) priv->add_ba_param.tx_win_size = pmadapter->coex_tx_win_size; if (tx_win_size != priv->add_ba_param.tx_win_size) { if (priv->media_connected == MTRUE) { for (i = 0; i < MAX_NUM_TID; i++) wlan_send_delba_txbastream_tbl( priv, i); wlan_recv_event( priv, MLAN_EVENT_ID_DRV_DEFER_HANDLING, MNULL); } } } } LEAVE(); return; } /** * @brief Get supported MCS set * * @param pmadapter A pointer to mlan_adapter structure * @param pioctl_req A pointer to ioctl request buffer * * @return MLAN_STATUS_SUCCESS --success, otherwise fail */ static mlan_status wlan_11n_ioctl_supported_mcs_set(pmlan_adapter pmadapter, pmlan_ioctl_req pioctl_req) { mlan_ds_11n_cfg *cfg = MNULL; int rx_mcs_supp; t_u8 mcs_set[NUM_MCS_FIELD]; mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; ENTER(); if (pioctl_req->action == MLAN_ACT_SET) { PRINTM(MERROR, "Set operation is not supported\n"); pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID; LEAVE(); return MLAN_STATUS_FAILURE; } rx_mcs_supp = GET_RXMCSSUPP(pmpriv->usr_dev_mcs_support); /* Set MCS for 1x1/2x2*/ memset(pmadapter, (t_u8 *)mcs_set, 0xff, rx_mcs_supp); /* Clear all the other values */ memset(pmadapter, (t_u8 *)&mcs_set[rx_mcs_supp], 0, NUM_MCS_FIELD - rx_mcs_supp); /* Set MCS32 with 40MHz support */ if ((ISSUPP_CHANWIDTH40(pmpriv->usr_dot_11n_dev_cap_bg) || ISSUPP_CHANWIDTH40(pmpriv->usr_dot_11n_dev_cap_a)) && !(pmpriv->curr_chan_flags & CHAN_FLAGS_NO_HT40PLUS && pmpriv->curr_chan_flags & CHAN_FLAGS_NO_HT40MINUS) && pmadapter->init_para.mcs32 == 1) SETHT_MCS32(mcs_set); cfg = (mlan_ds_11n_cfg *)pioctl_req->pbuf; memcpy_ext(pmadapter, cfg->param.supported_mcs_set, mcs_set, NUM_MCS_FIELD, NUM_MCS_FIELD); LEAVE(); return MLAN_STATUS_SUCCESS; } /** * @brief This function checks if the given pointer is valid entry of * Tx BA Stream table * * @param priv Pointer to mlan_private * @param ptxtblptr Pointer to tx ba stream entry * * @return MTRUE or MFALSE */ static int wlan_is_txbastreamptr_valid(mlan_private *priv, TxBAStreamTbl *ptxtblptr) { TxBAStreamTbl *ptx_tbl; ENTER(); ptx_tbl = (TxBAStreamTbl *)util_peek_list(priv->adapter->pmoal_handle, &priv->tx_ba_stream_tbl_ptr, MNULL, MNULL); if (!ptx_tbl) { LEAVE(); return MFALSE; } while (ptx_tbl != (TxBAStreamTbl *)&priv->tx_ba_stream_tbl_ptr) { if (ptx_tbl == ptxtblptr) { LEAVE(); return MTRUE; } ptx_tbl = ptx_tbl->pnext; } LEAVE(); return MFALSE; } /** * @brief This function will return the pointer to a entry in BA Stream * table which matches the ba_status requested * * @param priv A pointer to mlan_private * @param ba_status Current status of the BA stream * * @return A pointer to first entry matching status in BA stream * NULL if not found */ static TxBAStreamTbl *wlan_11n_get_txbastream_status(mlan_private *priv, baStatus_e ba_status) { TxBAStreamTbl *ptx_tbl; ENTER(); ptx_tbl = (TxBAStreamTbl *)util_peek_list(priv->adapter->pmoal_handle, &priv->tx_ba_stream_tbl_ptr, MNULL, MNULL); if (!ptx_tbl) { LEAVE(); return MNULL; } while (ptx_tbl != (TxBAStreamTbl *)&priv->tx_ba_stream_tbl_ptr) { if (ptx_tbl->ba_status == ba_status) { LEAVE(); return ptx_tbl; } ptx_tbl = ptx_tbl->pnext; } LEAVE(); return MNULL; } /******************************************************** Global Functions ********************************************************/ #ifdef STA_SUPPORT /** * @brief This function fills the cap info * * @param priv A pointer to mlan_private structure * @param pht_cap A pointer to MrvlIETypes_HTCap_t structure * @param bands Band configuration * * @return N/A */ static void wlan_fill_cap_info(mlan_private *priv, HTCap_t *ht_cap, t_u16 bands) { t_u32 usr_dot_11n_dev_cap; ENTER(); if (bands & BAND_A) usr_dot_11n_dev_cap = priv->usr_dot_11n_dev_cap_a; else usr_dot_11n_dev_cap = priv->usr_dot_11n_dev_cap_bg; if (ISSUPP_CHANWIDTH40(usr_dot_11n_dev_cap)) SETHT_SUPPCHANWIDTH(ht_cap->ht_cap_info); else RESETHT_SUPPCHANWIDTH(ht_cap->ht_cap_info); if (ISSUPP_GREENFIELD(usr_dot_11n_dev_cap)) SETHT_GREENFIELD(ht_cap->ht_cap_info); else RESETHT_GREENFIELD(ht_cap->ht_cap_info); if (ISSUPP_SHORTGI20(usr_dot_11n_dev_cap)) SETHT_SHORTGI20(ht_cap->ht_cap_info); else RESETHT_SHORTGI20(ht_cap->ht_cap_info); if (ISSUPP_SHORTGI40(usr_dot_11n_dev_cap)) SETHT_SHORTGI40(ht_cap->ht_cap_info); else RESETHT_SHORTGI40(ht_cap->ht_cap_info); if (ISSUPP_RXSTBC(usr_dot_11n_dev_cap)) SETHT_RXSTBC(ht_cap->ht_cap_info, 1); else RESETHT_RXSTBC(ht_cap->ht_cap_info); if (ISENABLED_40MHZ_INTOLARENT(usr_dot_11n_dev_cap)) SETHT_40MHZ_INTOLARANT(ht_cap->ht_cap_info); else RESETHT_40MHZ_INTOLARANT(ht_cap->ht_cap_info); /** if current channel only allow 20Mhz, we should cler 40Mhz support */ if (priv->curr_chan_flags & CHAN_FLAGS_NO_HT40PLUS && priv->curr_chan_flags & CHAN_FLAGS_NO_HT40MINUS) { RESETHT_SUPPCHANWIDTH(ht_cap->ht_cap_info); RESETHT_SHORTGI40(ht_cap->ht_cap_info); RESETHT_40MHZ_INTOLARANT(ht_cap->ht_cap_info); } /* No user config for LDPC coding capability yet */ if (ISSUPP_RXLDPC(usr_dot_11n_dev_cap)) SETHT_LDPCCODINGCAP(ht_cap->ht_cap_info); else RESETHT_LDPCCODINGCAP(ht_cap->ht_cap_info); /* No user config for TX STBC yet */ if (ISSUPP_TXSTBC(usr_dot_11n_dev_cap)) SETHT_TXSTBC(ht_cap->ht_cap_info); else RESETHT_TXSTBC(ht_cap->ht_cap_info); /* No user config for Delayed BACK yet */ RESETHT_DELAYEDBACK(ht_cap->ht_cap_info); /* Need change to support 8k AMSDU receive */ RESETHT_MAXAMSDU(ht_cap->ht_cap_info); /* SM power save */ RESETHT_SM_POWERSAVE(ht_cap->ht_cap_info); /* Clear to HT SMPS static mode*/ if (ISSUPP_MIMOPS(usr_dot_11n_dev_cap)) { if (ISSUPP_SMPS_DYNAMIC_MODE(usr_dot_11n_dev_cap)) SETHT_SMPS_DYNAMIC(ht_cap->ht_cap_info); /* Set to HT SMPS dynamic mode */ } else { SETHT_SMPS_DISABLE(ht_cap->ht_cap_info); /* Disable HT SMPS */ } LEAVE(); } /** * @brief This function clear the bit in cap info which we don't support * * @param priv A pointer to mlan_private structure * @param pht_cap A pointer to MrvlIETypes_HTCap_t structure * @param bands Band configuration * * @return N/A */ static void wlan_reset_cap_info(mlan_private *priv, HTCap_t *ht_cap, t_u16 bands) { t_u32 usr_dot_11n_dev_cap; ENTER(); if (bands & BAND_A) usr_dot_11n_dev_cap = priv->usr_dot_11n_dev_cap_a; else usr_dot_11n_dev_cap = priv->usr_dot_11n_dev_cap_bg; if (!ISSUPP_CHANWIDTH40(usr_dot_11n_dev_cap)) RESETHT_SUPPCHANWIDTH(ht_cap->ht_cap_info); if (!ISSUPP_GREENFIELD(usr_dot_11n_dev_cap)) RESETHT_GREENFIELD(ht_cap->ht_cap_info); if (!ISSUPP_SHORTGI20(usr_dot_11n_dev_cap)) RESETHT_SHORTGI20(ht_cap->ht_cap_info); if (!ISSUPP_SHORTGI40(usr_dot_11n_dev_cap)) RESETHT_SHORTGI40(ht_cap->ht_cap_info); if (!ISSUPP_RXSTBC(usr_dot_11n_dev_cap)) RESETHT_RXSTBC(ht_cap->ht_cap_info); if (!ISENABLED_40MHZ_INTOLARENT(usr_dot_11n_dev_cap)) RESETHT_40MHZ_INTOLARANT(ht_cap->ht_cap_info); /** if current channel only allow 20Mhz, we should cler 40Mhz support */ if (priv->curr_chan_flags & CHAN_FLAGS_NO_HT40PLUS && priv->curr_chan_flags & CHAN_FLAGS_NO_HT40MINUS) { RESETHT_SUPPCHANWIDTH(ht_cap->ht_cap_info); RESETHT_SHORTGI40(ht_cap->ht_cap_info); RESETHT_40MHZ_INTOLARANT(ht_cap->ht_cap_info); } /* No user config for LDPC coding capability yet */ if (!ISSUPP_RXLDPC(usr_dot_11n_dev_cap)) RESETHT_LDPCCODINGCAP(ht_cap->ht_cap_info); /* No user config for TX STBC yet */ if (!ISSUPP_TXSTBC(usr_dot_11n_dev_cap)) RESETHT_TXSTBC(ht_cap->ht_cap_info); /* No user config for Delayed BACK yet */ RESETHT_DELAYEDBACK(ht_cap->ht_cap_info); /* Need change to support 8k AMSDU receive */ RESETHT_MAXAMSDU(ht_cap->ht_cap_info); /* SM power save */ if (!ISSUPP_MIMOPS(usr_dot_11n_dev_cap)) SETHT_SMPS_DISABLE(ht_cap->ht_cap_info); /* Disable HT SMPS */ LEAVE(); } /** * @brief This function fills the HT cap tlv * * @param priv A pointer to mlan_private structure * @param pht_cap A pointer to MrvlIETypes_HTCap_t structure * @param bands Band configuration * @param fill A flag for fill the htcap info * * @return N/A */ void wlan_fill_ht_cap_tlv(mlan_private *priv, MrvlIETypes_HTCap_t *pht_cap, t_u16 bands, t_u8 fill) { mlan_adapter *pmadapter = priv->adapter; int rx_mcs_supp; t_u32 usr_dot_11n_dev_cap; ENTER(); if (bands & BAND_A) usr_dot_11n_dev_cap = priv->usr_dot_11n_dev_cap_a; else usr_dot_11n_dev_cap = priv->usr_dot_11n_dev_cap_bg; /* Fill HT cap info */ if (fill) wlan_fill_cap_info(priv, &pht_cap->ht_cap, bands); else wlan_reset_cap_info(priv, &pht_cap->ht_cap, bands); pht_cap->ht_cap.ht_cap_info = wlan_cpu_to_le16(pht_cap->ht_cap.ht_cap_info); /* Set ampdu param */ SETAMPDU_SIZE(pht_cap->ht_cap.ampdu_param, AMPDU_FACTOR_64K); SETAMPDU_SPACING(pht_cap->ht_cap.ampdu_param, pmadapter->hw_mpdu_density); rx_mcs_supp = GET_RXMCSSUPP(priv->usr_dev_mcs_support); #if defined(PCIE9098) || defined(SD9098) || defined(USB9098) || \ defined(PCIE9097) || defined(USB9097) || defined(SDIW624) || \ defined(PCIEIW624) || defined(USBIW624) || defined(SD9097) if (IS_CARD9098(pmadapter->card_type) || IS_CARDIW624(pmadapter->card_type) || IS_CARD9097(pmadapter->card_type) || IS_CARDAW693(pmadapter->card_type)) { if (bands & BAND_A) rx_mcs_supp = MIN( rx_mcs_supp, GET_RXMCSSUPP(pmadapter->user_htstream >> 8)); else rx_mcs_supp = MIN(rx_mcs_supp, GET_RXMCSSUPP(pmadapter->user_htstream)); } #endif memset(pmadapter, (t_u8 *)pht_cap->ht_cap.supported_mcs_set, 0xff, rx_mcs_supp); /* Clear all the other values to get the minimum mcs set btw STA and AP */ memset(pmadapter, (t_u8 *)&pht_cap->ht_cap.supported_mcs_set[rx_mcs_supp], 0, NUM_MCS_FIELD - rx_mcs_supp); /* Set MCS32 with 40MHz support */ /* if current channel only support 20MHz, we should not set 40Mz * supprot*/ if (ISSUPP_CHANWIDTH40(usr_dot_11n_dev_cap) && !(priv->curr_chan_flags & CHAN_FLAGS_NO_HT40PLUS && priv->curr_chan_flags & CHAN_FLAGS_NO_HT40MINUS) && pmadapter->init_para.mcs32 == 1) SETHT_MCS32(pht_cap->ht_cap.supported_mcs_set); /* Clear RD responder bit */ RESETHT_EXTCAP_RDG(pht_cap->ht_cap.ht_ext_cap); pht_cap->ht_cap.ht_ext_cap = wlan_cpu_to_le16(pht_cap->ht_cap.ht_ext_cap); /* Set Tx BF cap */ pht_cap->ht_cap.tx_bf_cap = wlan_cpu_to_le32(priv->tx_bf_cap); LEAVE(); return; } /** * @brief This function fills the HT cap ie * * @param priv A pointer to mlan_private structure * @param pht_cap A pointer to IEEEtypes_HTCap_t structure * @param bands Band configuration * * @return N/A */ void wlan_fill_ht_cap_ie(mlan_private *priv, IEEEtypes_HTCap_t *pht_cap, t_u16 bands) { mlan_adapter *pmadapter = priv->adapter; int rx_mcs_supp; t_u32 usr_dot_11n_dev_cap; ENTER(); pht_cap->ieee_hdr.element_id = HT_CAPABILITY; pht_cap->ieee_hdr.len = sizeof(HTCap_t); if (bands & BAND_A) usr_dot_11n_dev_cap = priv->usr_dot_11n_dev_cap_a; else usr_dot_11n_dev_cap = priv->usr_dot_11n_dev_cap_bg; /* Fill HT cap info */ wlan_fill_cap_info(priv, &pht_cap->ht_cap, bands); /* Set ampdu param */ SETAMPDU_SIZE(pht_cap->ht_cap.ampdu_param, AMPDU_FACTOR_64K); SETAMPDU_SPACING(pht_cap->ht_cap.ampdu_param, 0); rx_mcs_supp = GET_RXMCSSUPP(priv->usr_dev_mcs_support); #if defined(PCIE9098) || defined(SD9098) || defined(USB9098) || \ defined(PCIE9097) || defined(USB9097) || defined(SDIW624) || \ defined(PCIEIW624) || defined(USBIW624) || defined(SD9097) if (IS_CARD9098(pmadapter->card_type) || IS_CARDIW624(pmadapter->card_type) || IS_CARD9097(pmadapter->card_type) || IS_CARDAW693(pmadapter->card_type)) { if (bands & BAND_A) rx_mcs_supp = MIN( rx_mcs_supp, GET_RXMCSSUPP(pmadapter->user_htstream >> 8)); else rx_mcs_supp = MIN(rx_mcs_supp, GET_RXMCSSUPP(pmadapter->user_htstream)); } #endif memset(pmadapter, (t_u8 *)pht_cap->ht_cap.supported_mcs_set, 0xff, rx_mcs_supp); /* Clear all the other values to get the minimum mcs set btw STA and AP */ memset(pmadapter, (t_u8 *)&pht_cap->ht_cap.supported_mcs_set[rx_mcs_supp], 0, NUM_MCS_FIELD - rx_mcs_supp); /* Set MCS32 with 40MHz support */ /* if current channel only support 20MHz, we should not set 40Mz * supprot*/ if (ISSUPP_CHANWIDTH40(usr_dot_11n_dev_cap) && !(priv->curr_chan_flags & CHAN_FLAGS_NO_HT40PLUS && priv->curr_chan_flags & CHAN_FLAGS_NO_HT40MINUS) && pmadapter->init_para.mcs32 == 1) SETHT_MCS32(pht_cap->ht_cap.supported_mcs_set); /* Clear RD responder bit */ RESETHT_EXTCAP_RDG(pht_cap->ht_cap.ht_ext_cap); /* Set Tx BF cap */ pht_cap->ht_cap.tx_bf_cap = priv->tx_bf_cap; LEAVE(); return; } #endif /* STA_SUPPORT */ /** * @brief This function prints the 802.11n device capability * * @param pmadapter A pointer to mlan_adapter structure * @param cap Capability value * * @return N/A */ void wlan_show_dot11ndevcap(pmlan_adapter pmadapter, t_u32 cap) { ENTER(); PRINTM(MINFO, "GET_HW_SPEC: Maximum MSDU length = %s octets\n", (ISSUPP_MAXAMSDU(cap) ? "7935" : "3839")); PRINTM(MINFO, "GET_HW_SPEC: Beam forming %s\n", (ISSUPP_BEAMFORMING(cap) ? "supported" : "not supported")); PRINTM(MINFO, "GET_HW_SPEC: Greenfield preamble %s\n", (ISSUPP_GREENFIELD(cap) ? "supported" : "not supported")); PRINTM(MINFO, "GET_HW_SPEC: AMPDU %s\n", (ISSUPP_AMPDU(cap) ? "supported" : "not supported")); PRINTM(MINFO, "GET_HW_SPEC: MIMO Power Save %s\n", (ISSUPP_MIMOPS(cap) ? "supported" : "not supported")); PRINTM(MINFO, "GET_HW_SPEC: Rx STBC %s\n", (ISSUPP_RXSTBC(cap) ? "supported" : "not supported")); PRINTM(MINFO, "GET_HW_SPEC: Tx STBC %s\n", (ISSUPP_TXSTBC(cap) ? "supported" : "not supported")); PRINTM(MINFO, "GET_HW_SPEC: Short GI for 40 Mhz %s\n", (ISSUPP_SHORTGI40(cap) ? "supported" : "not supported")); PRINTM(MINFO, "GET_HW_SPEC: Short GI for 20 Mhz %s\n", (ISSUPP_SHORTGI20(cap) ? "supported" : "not supported")); PRINTM(MINFO, "GET_HW_SPEC: LDPC coded packet receive %s\n", (ISSUPP_RXLDPC(cap) ? "supported" : "not supported")); PRINTM(MINFO, "GET_HW_SPEC: Number of Tx BA streams supported = %d\n", ISSUPP_GETTXBASTREAM(cap)); PRINTM(MINFO, "GET_HW_SPEC: 40 Mhz channel width %s\n", (ISSUPP_CHANWIDTH40(cap) ? "supported" : "not supported")); PRINTM(MINFO, "GET_HW_SPEC: 20 Mhz channel width %s\n", (ISSUPP_CHANWIDTH20(cap) ? "supported" : "not supported")); PRINTM(MINFO, "GET_HW_SPEC: 10 Mhz channel width %s\n", (ISSUPP_CHANWIDTH10(cap) ? "supported" : "not supported")); if (ISSUPP_RXANTENNAA(cap)) PRINTM(MINFO, "GET_HW_SPEC: Presence of Rx antenna A\n"); if (ISSUPP_RXANTENNAB(cap)) PRINTM(MINFO, "GET_HW_SPEC: Presence of Rx antenna B\n"); if (ISSUPP_RXANTENNAC(cap)) PRINTM(MINFO, "GET_HW_SPEC: Presence of Rx antenna C\n"); if (ISSUPP_RXANTENNAD(cap)) PRINTM(MINFO, "GET_HW_SPEC: Presence of Rx antenna D\n"); if (ISSUPP_TXANTENNAA(cap)) PRINTM(MINFO, "GET_HW_SPEC: Presence of Tx antenna A\n"); if (ISSUPP_TXANTENNAB(cap)) PRINTM(MINFO, "GET_HW_SPEC: Presence of Tx antenna B\n"); if (ISSUPP_TXANTENNAC(cap)) PRINTM(MINFO, "GET_HW_SPEC: Presence of Tx antenna C\n"); if (ISSUPP_TXANTENNAD(cap)) PRINTM(MINFO, "GET_HW_SPEC: Presence of Tx antenna D\n"); LEAVE(); return; } /** * @brief This function prints the 802.11n device MCS * * @param pmadapter A pointer to mlan_adapter structure * @param support Support value * * @return N/A */ void wlan_show_devmcssupport(pmlan_adapter pmadapter, t_u8 support) { ENTER(); PRINTM(MINFO, "GET_HW_SPEC: MCSs for %dx%d MIMO\n", GET_RXMCSSUPP(support), GET_TXMCSSUPP(support)); LEAVE(); return; } /** * @brief This function handles the command response of * delete a block ack request * * @param priv A pointer to mlan_private structure * @param resp A pointer to HostCmd_DS_COMMAND * * @return MLAN_STATUS_SUCCESS */ mlan_status wlan_ret_11n_delba(mlan_private *priv, HostCmd_DS_COMMAND *resp) { int tid; TxBAStreamTbl *ptx_ba_tbl; HostCmd_DS_11N_DELBA *pdel_ba = (HostCmd_DS_11N_DELBA *)&resp->params.del_ba; ENTER(); pdel_ba->del_ba_param_set = wlan_le16_to_cpu(pdel_ba->del_ba_param_set); pdel_ba->reason_code = wlan_le16_to_cpu(pdel_ba->reason_code); tid = pdel_ba->del_ba_param_set >> DELBA_TID_POS; if (pdel_ba->del_result == BA_RESULT_SUCCESS) { mlan_11n_delete_bastream_tbl( priv, tid, pdel_ba->peer_mac_addr, TYPE_DELBA_SENT, INITIATOR_BIT(pdel_ba->del_ba_param_set), 0); wlan_request_ralist_lock(priv); ptx_ba_tbl = wlan_11n_get_txbastream_status( priv, BA_STREAM_SETUP_INPROGRESS); wlan_release_ralist_lock(priv); if (ptx_ba_tbl) { /* mark this txba stream as waiting addba resp, in case * we send duplicate addba req command */ ptx_ba_tbl->ba_status = BA_STREAM_SETUP_SENT_ADDBA; wlan_send_addba(priv, ptx_ba_tbl->tid, ptx_ba_tbl->ra); } } else { /* * In case of failure, recreate * the deleted stream in case * we initiated the ADDBA */ if (INITIATOR_BIT(pdel_ba->del_ba_param_set)) { wlan_request_ralist_lock(priv); wlan_11n_set_txbastream_status(priv, tid, pdel_ba->peer_mac_addr, BA_STREAM_SETUP_COMPLETE, MFALSE); ptx_ba_tbl = wlan_11n_get_txbastream_status( priv, BA_STREAM_SETUP_INPROGRESS); wlan_release_ralist_lock(priv); if (ptx_ba_tbl) { mlan_11n_delete_bastream_tbl( priv, ptx_ba_tbl->tid, ptx_ba_tbl->ra, TYPE_DELBA_SENT, MTRUE, 0); } } } LEAVE(); return MLAN_STATUS_SUCCESS; } /** * @brief This function handles the command response of * add a block ack request * * @param priv A pointer to mlan_private structure * @param resp A pointer to HostCmd_DS_COMMAND * * @return MLAN_STATUS_SUCCESS */ mlan_status wlan_ret_11n_addba_req(mlan_private *priv, HostCmd_DS_COMMAND *resp) { t_u8 tid; HostCmd_DS_11N_ADDBA_RSP *padd_ba_rsp = (HostCmd_DS_11N_ADDBA_RSP *)&resp->params.add_ba_rsp; TxBAStreamTbl *ptx_ba_tbl; raListTbl *ra_list = MNULL; int tid_down; ENTER(); padd_ba_rsp->block_ack_param_set = wlan_le16_to_cpu(padd_ba_rsp->block_ack_param_set); padd_ba_rsp->block_ack_tmo = wlan_le16_to_cpu(padd_ba_rsp->block_ack_tmo); padd_ba_rsp->ssn = (wlan_le16_to_cpu(padd_ba_rsp->ssn)) & SSN_MASK; padd_ba_rsp->status_code = wlan_le16_to_cpu(padd_ba_rsp->status_code); tid = (padd_ba_rsp->block_ack_param_set & BLOCKACKPARAM_TID_MASK) >> BLOCKACKPARAM_TID_POS; tid_down = wlan_get_wmm_tid_down(priv, tid); ra_list = wlan_wmm_get_ralist_node(priv, tid_down, padd_ba_rsp->peer_mac_addr); if (padd_ba_rsp->status_code == BA_RESULT_SUCCESS) { ptx_ba_tbl = wlan_11n_get_txbastream_tbl( priv, tid, padd_ba_rsp->peer_mac_addr, MTRUE); if (ptx_ba_tbl) { PRINTM(MCMND, "ADDBA REQ: " MACSTR " tid=%d ssn=%d win_size=%d,amsdu=%d\n", MAC2STR(padd_ba_rsp->peer_mac_addr), tid, padd_ba_rsp->ssn, ((padd_ba_rsp->block_ack_param_set & BLOCKACKPARAM_WINSIZE_MASK) >> BLOCKACKPARAM_WINSIZE_POS), padd_ba_rsp->block_ack_param_set & BLOCKACKPARAM_AMSDU_SUPP_MASK); ptx_ba_tbl->ba_status = BA_STREAM_SETUP_COMPLETE; if ((padd_ba_rsp->block_ack_param_set & BLOCKACKPARAM_AMSDU_SUPP_MASK) && priv->add_ba_param.tx_amsdu && (priv->aggr_prio_tbl[tid].amsdu != BA_STREAM_NOT_ALLOWED)) ptx_ba_tbl->amsdu = MTRUE; else ptx_ba_tbl->amsdu = MFALSE; if (ra_list) { ra_list->amsdu_in_ampdu = ptx_ba_tbl->amsdu; ra_list->ba_status = BA_STREAM_SETUP_COMPLETE; } } else { PRINTM(MERROR, "BA stream not created\n"); } } else { if (ra_list) { ra_list->amsdu_in_ampdu = MFALSE; ra_list->ba_status = BA_STREAM_NOT_SETUP; } mlan_11n_delete_bastream_tbl(priv, tid, padd_ba_rsp->peer_mac_addr, TYPE_DELBA_SENT, MTRUE, 0); if (padd_ba_rsp->add_rsp_result != BA_RESULT_TIMEOUT) { #ifdef UAP_SUPPORT if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) disable_station_ampdu( priv, tid, padd_ba_rsp->peer_mac_addr); #endif /* UAP_SUPPORT */ if (ra_list && ra_list->is_tdls_link) disable_station_ampdu( priv, tid, padd_ba_rsp->peer_mac_addr); priv->aggr_prio_tbl[tid].ampdu_ap = BA_STREAM_NOT_ALLOWED; } else { t_u8 event_buf[256]; mlan_event *pevent = (mlan_event *)event_buf; addba_timeout_event *evtbuf = (addba_timeout_event *)pevent->event_buf; if (ra_list) { ra_list->packet_count = 0; ra_list->ba_packet_threshold = wlan_get_random_ba_threshold( priv->adapter); } memset(priv->adapter, event_buf, 0x00, sizeof(event_buf)); pevent->bss_index = priv->bss_index; pevent->event_id = MLAN_EVENT_ID_DRV_ADDBA_TIMEOUT; pevent->event_len = sizeof(addba_timeout_event); memcpy_ext(priv->adapter, evtbuf->peer_mac_addr, padd_ba_rsp->peer_mac_addr, MLAN_MAC_ADDR_LENGTH, MLAN_MAC_ADDR_LENGTH); evtbuf->tid = tid; wlan_recv_event(priv, MLAN_EVENT_ID_DRV_ADDBA_TIMEOUT, pevent); } } LEAVE(); return MLAN_STATUS_SUCCESS; } /** * @brief This function restore tx_pause flag * * @param priv A pointer to mlan_private structure * @param flag MTRUE/MFALSE; * * @return N/A */ void wlan_set_tx_pause_flag(mlan_private *priv, t_u8 flag) { mlan_private *pmpriv = MNULL; t_u8 i; for (i = 0; i < priv->adapter->priv_num; i++) { pmpriv = priv->adapter->priv[i]; if (pmpriv) pmpriv->tx_pause = flag; } } /** * @brief This function prepares command of reconfigure tx buf * * @param priv A pointer to mlan_private structure * @param cmd A pointer to HostCmd_DS_COMMAND structure * @param cmd_action The action: GET or SET * @param pdata_buf A pointer to data buffer * * @return MLAN_STATUS_SUCCESS */ mlan_status wlan_cmd_recfg_tx_buf(mlan_private *priv, HostCmd_DS_COMMAND *cmd, int cmd_action, void *pdata_buf) { HostCmd_DS_TXBUF_CFG *ptx_buf = &cmd->params.tx_buf; t_u16 action = (t_u16)cmd_action; t_u16 buf_size = *((t_u16 *)pdata_buf); ENTER(); cmd->command = wlan_cpu_to_le16(HostCmd_CMD_RECONFIGURE_TX_BUFF); cmd->size = wlan_cpu_to_le16(sizeof(HostCmd_DS_TXBUF_CFG) + S_DS_GEN); ptx_buf->action = wlan_cpu_to_le16(action); switch (action) { case HostCmd_ACT_GEN_SET: PRINTM(MCMND, "set tx_buf = %d\n", buf_size); ptx_buf->buff_size = wlan_cpu_to_le16(buf_size); /** stop tx traffic */ wlan_set_tx_pause_flag(priv, MTRUE); break; case HostCmd_ACT_GEN_GET: default: ptx_buf->buff_size = 0; break; } LEAVE(); return MLAN_STATUS_SUCCESS; } /** * @brief This function prepares command of amsdu aggr control * * @param priv A pointer to mlan_private structure * @param cmd A pointer to HostCmd_DS_COMMAND structure * @param cmd_action The action: GET or SET * @param pdata_buf A pointer to data buffer * * @return MLAN_STATUS_SUCCESS */ mlan_status wlan_cmd_amsdu_aggr_ctrl(mlan_private *priv, HostCmd_DS_COMMAND *cmd, int cmd_action, void *pdata_buf) { HostCmd_DS_AMSDU_AGGR_CTRL *pamsdu_ctrl = &cmd->params.amsdu_aggr_ctrl; t_u16 action = (t_u16)cmd_action; mlan_ds_11n_amsdu_aggr_ctrl *aa_ctrl = (mlan_ds_11n_amsdu_aggr_ctrl *)pdata_buf; ENTER(); cmd->command = wlan_cpu_to_le16(HostCmd_CMD_AMSDU_AGGR_CTRL); cmd->size = wlan_cpu_to_le16(sizeof(HostCmd_DS_AMSDU_AGGR_CTRL) + S_DS_GEN); pamsdu_ctrl->action = wlan_cpu_to_le16(action); switch (action) { case HostCmd_ACT_GEN_SET: pamsdu_ctrl->enable = wlan_cpu_to_le16(aa_ctrl->enable); pamsdu_ctrl->curr_buf_size = 0; break; case HostCmd_ACT_GEN_GET: default: pamsdu_ctrl->curr_buf_size = 0; break; } LEAVE(); return MLAN_STATUS_SUCCESS; } /** * @brief This function handles the command response of amsdu aggr ctrl * * @param pmpriv A pointer to mlan_private structure * @param resp A pointer to HostCmd_DS_COMMAND * @param pioctl_buf A pointer to mlan_ioctl_req structure * * @return MLAN_STATUS_SUCCESS */ mlan_status wlan_ret_amsdu_aggr_ctrl(pmlan_private pmpriv, HostCmd_DS_COMMAND *resp, mlan_ioctl_req *pioctl_buf) { mlan_ds_11n_cfg *cfg = MNULL; HostCmd_DS_AMSDU_AGGR_CTRL *amsdu_ctrl = &resp->params.amsdu_aggr_ctrl; ENTER(); if (pioctl_buf) { cfg = (mlan_ds_11n_cfg *)pioctl_buf->pbuf; cfg->param.amsdu_aggr_ctrl.enable = wlan_le16_to_cpu(amsdu_ctrl->enable); cfg->param.amsdu_aggr_ctrl.curr_buf_size = wlan_le16_to_cpu(amsdu_ctrl->curr_buf_size); } LEAVE(); return MLAN_STATUS_SUCCESS; } /** * @brief This function prepares 11n cfg command * * @param pmpriv A pointer to mlan_private structure * @param cmd A pointer to HostCmd_DS_COMMAND structure * @param cmd_action the action: GET or SET * @param pdata_buf A pointer to data buffer * @return MLAN_STATUS_SUCCESS */ mlan_status wlan_cmd_11n_cfg(pmlan_private pmpriv, HostCmd_DS_COMMAND *cmd, t_u16 cmd_action, t_void *pdata_buf) { HostCmd_DS_11N_CFG *htcfg = &cmd->params.htcfg; mlan_ds_11n_tx_cfg *txcfg = (mlan_ds_11n_tx_cfg *)pdata_buf; ENTER(); cmd->command = wlan_cpu_to_le16(HostCmd_CMD_11N_CFG); cmd->size = wlan_cpu_to_le16(sizeof(HostCmd_DS_11N_CFG) + S_DS_GEN); htcfg->action = wlan_cpu_to_le16(cmd_action); htcfg->ht_tx_cap = wlan_cpu_to_le16(txcfg->httxcap); htcfg->ht_tx_info = wlan_cpu_to_le16(txcfg->httxinfo); htcfg->misc_config = wlan_cpu_to_le16(txcfg->misc_cfg); LEAVE(); return MLAN_STATUS_SUCCESS; } /** * @brief This function handles the command response of 11ncfg * * @param pmpriv A pointer to mlan_private structure * @param resp A pointer to HostCmd_DS_COMMAND * @param pioctl_buf A pointer to mlan_ioctl_req structure * * @return MLAN_STATUS_SUCCESS */ mlan_status wlan_ret_11n_cfg(pmlan_private pmpriv, HostCmd_DS_COMMAND *resp, mlan_ioctl_req *pioctl_buf) { mlan_ds_11n_cfg *cfg = MNULL; HostCmd_DS_11N_CFG *htcfg = &resp->params.htcfg; ENTER(); if (pioctl_buf && (wlan_le16_to_cpu(htcfg->action) == HostCmd_ACT_GEN_GET)) { cfg = (mlan_ds_11n_cfg *)pioctl_buf->pbuf; cfg->param.tx_cfg.httxcap = wlan_le16_to_cpu(htcfg->ht_tx_cap); cfg->param.tx_cfg.httxinfo = wlan_le16_to_cpu(htcfg->ht_tx_info); cfg->param.tx_cfg.misc_cfg = wlan_le16_to_cpu(htcfg->misc_config); } LEAVE(); return MLAN_STATUS_SUCCESS; } /** * @brief This function prepares reject addba req command * * @param pmpriv A pointer to mlan_private structure * @param cmd A pointer to HostCmd_DS_COMMAND structure * @param cmd_action the action: GET or SET * @param pdata_buf A pointer to data buffer * @return MLAN_STATUS_SUCCESS */ mlan_status wlan_cmd_reject_addba_req(pmlan_private pmpriv, HostCmd_DS_COMMAND *cmd, t_u16 cmd_action, t_void *pdata_buf) { HostCmd_DS_REJECT_ADDBA_REQ *preject_addba_req = &cmd->params.rejectaddbareq; mlan_ds_reject_addba_req *prejaddbareq = (mlan_ds_reject_addba_req *)pdata_buf; ENTER(); cmd->command = wlan_cpu_to_le16(HostCmd_CMD_REJECT_ADDBA_REQ); cmd->size = wlan_cpu_to_le16(sizeof(HostCmd_DS_REJECT_ADDBA_REQ) + S_DS_GEN); preject_addba_req->action = wlan_cpu_to_le16(cmd_action); preject_addba_req->conditions = wlan_cpu_to_le32(prejaddbareq->conditions); LEAVE(); return MLAN_STATUS_SUCCESS; } /** * @brief This function handles the command response of reject addba req * * @param pmpriv A pointer to mlan_private structure * @param resp A pointer to HostCmd_DS_COMMAND * @param pioctl_buf A pointer to mlan_ioctl_req structure * * @return MLAN_STATUS_SUCCESS */ mlan_status wlan_ret_reject_addba_req(pmlan_private pmpriv, HostCmd_DS_COMMAND *resp, mlan_ioctl_req *pioctl_buf) { mlan_ds_11n_cfg *cfg = MNULL; HostCmd_DS_REJECT_ADDBA_REQ *preject_addba_req = &resp->params.rejectaddbareq; ENTER(); if (pioctl_buf && (wlan_le16_to_cpu(preject_addba_req->action) == HostCmd_ACT_GEN_GET)) { cfg = (mlan_ds_11n_cfg *)pioctl_buf->pbuf; cfg->param.reject_addba_req.conditions = wlan_le32_to_cpu(preject_addba_req->conditions); } LEAVE(); return MLAN_STATUS_SUCCESS; } /** * @brief This function prepares TX BF configuration command * * @param pmpriv A pointer to mlan_private structure * @param cmd A pointer to HostCmd_DS_COMMAND structure * @param cmd_action The action: GET or SET * @param pdata_buf A pointer to data buffer * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE */ mlan_status wlan_cmd_tx_bf_cfg(pmlan_private pmpriv, HostCmd_DS_COMMAND *cmd, t_u16 cmd_action, t_void *pdata_buf) { pmlan_adapter pmadapter = pmpriv->adapter; HostCmd_DS_TX_BF_CFG *txbfcfg = &cmd->params.tx_bf_cfg; mlan_ds_11n_tx_bf_cfg *txbf = (mlan_ds_11n_tx_bf_cfg *)pdata_buf; ENTER(); cmd->command = wlan_cpu_to_le16(HostCmd_CMD_TX_BF_CFG); cmd->size = wlan_cpu_to_le16(sizeof(HostCmd_DS_TX_BF_CFG) + S_DS_GEN); if (txbf->bf_action == SET_GET_BF_PERIODICITY) { memcpy_ext(pmadapter, txbfcfg->body.bf_periodicity.peer_mac, txbf->body.bf_periodicity[0].peer_mac, MLAN_MAC_ADDR_LENGTH, MLAN_MAC_ADDR_LENGTH); } txbfcfg->action = wlan_cpu_to_le16(txbf->action); txbfcfg->bf_action = wlan_cpu_to_le16(txbf->bf_action); if (cmd_action == HostCmd_ACT_GEN_SET) { switch (txbf->bf_action) { case BF_GLOBAL_CONFIGURATION: txbfcfg->body.bf_global_cfg.bf_enbl = txbf->body.bf_global_cfg.bf_enbl; txbfcfg->body.bf_global_cfg.sounding_enbl = txbf->body.bf_global_cfg.sounding_enbl; txbfcfg->body.bf_global_cfg.fb_type = txbf->body.bf_global_cfg.fb_type; txbfcfg->body.bf_global_cfg.snr_threshold = txbf->body.bf_global_cfg.snr_threshold; txbfcfg->body.bf_global_cfg.sounding_interval = wlan_cpu_to_le16(txbf->body.bf_global_cfg .sounding_interval); txbfcfg->body.bf_global_cfg.bf_mode = txbf->body.bf_global_cfg.bf_mode; break; case TRIGGER_SOUNDING_FOR_PEER: memcpy_ext(pmadapter, txbfcfg->body.bf_sound_args.peer_mac, txbf->body.bf_sound[0].peer_mac, MLAN_MAC_ADDR_LENGTH, MLAN_MAC_ADDR_LENGTH); break; case SET_GET_BF_PERIODICITY: txbfcfg->body.bf_periodicity.interval = wlan_cpu_to_le16( txbf->body.bf_periodicity->interval); break; case TX_BF_FOR_PEER_ENBL: memcpy_ext(pmadapter, txbfcfg->body.tx_bf_peer.peer_mac, txbf->body.tx_bf_peer[0].peer_mac, MLAN_MAC_ADDR_LENGTH, MLAN_MAC_ADDR_LENGTH); txbfcfg->body.tx_bf_peer.bf_enbl = txbf->body.tx_bf_peer[0].bf_enbl; txbfcfg->body.tx_bf_peer.sounding_enbl = txbf->body.tx_bf_peer[0].sounding_enbl; txbfcfg->body.tx_bf_peer.fb_type = txbf->body.tx_bf_peer[0].fb_type; break; case SET_SNR_THR_PEER: memcpy_ext(pmadapter, txbfcfg->body.bf_snr.peer_mac, txbf->body.bf_snr[0].peer_mac, MLAN_MAC_ADDR_LENGTH, MLAN_MAC_ADDR_LENGTH); txbfcfg->body.bf_snr.snr = txbf->body.bf_snr[0].snr; break; default: LEAVE(); return MLAN_STATUS_FAILURE; } } LEAVE(); return MLAN_STATUS_SUCCESS; } /** * @brief This function handles the command response * of TX BF configuration * * @param pmpriv A pointer to mlan_private structure * @param resp A pointer to HostCmd_DS_COMMAND * @param pioctl_buf A pointer to mlan_ioctl_req structure * * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE */ mlan_status wlan_ret_tx_bf_cfg(pmlan_private pmpriv, HostCmd_DS_COMMAND *resp, mlan_ioctl_req *pioctl_buf) { pmlan_adapter pmadapter = pmpriv->adapter; HostCmd_DS_TX_BF_CFG *txbfcfg = &resp->params.tx_bf_cfg; mlan_ds_11n_cfg *cfg_11n = MNULL; mlan_ds_11n_tx_bf_cfg *txbf = MNULL; bf_peer_args *tx_bf_peer; bf_snr_thr_t *bf_snr; int i; ENTER(); if (pioctl_buf) { cfg_11n = (mlan_ds_11n_cfg *)pioctl_buf->pbuf; txbf = (mlan_ds_11n_tx_bf_cfg *)&cfg_11n->param.tx_bf; txbf->bf_action = wlan_le16_to_cpu(txbfcfg->bf_action); switch (txbf->bf_action) { case BF_GLOBAL_CONFIGURATION: txbf->body.bf_global_cfg.bf_enbl = txbfcfg->body.bf_global_cfg.bf_enbl; txbf->body.bf_global_cfg.sounding_enbl = txbfcfg->body.bf_global_cfg.sounding_enbl; txbf->body.bf_global_cfg.fb_type = txbfcfg->body.bf_global_cfg.fb_type; txbf->body.bf_global_cfg.snr_threshold = txbfcfg->body.bf_global_cfg.snr_threshold; txbf->body.bf_global_cfg.sounding_interval = wlan_le16_to_cpu(txbfcfg->body.bf_global_cfg .sounding_interval); txbf->body.bf_global_cfg.bf_mode = txbfcfg->body.bf_global_cfg.bf_mode; break; case TRIGGER_SOUNDING_FOR_PEER: memcpy_ext(pmadapter, txbf->body.bf_sound[0].peer_mac, txbfcfg->body.bf_sound_args.peer_mac, MLAN_MAC_ADDR_LENGTH, MLAN_MAC_ADDR_LENGTH); txbf->body.bf_sound[0].status = txbfcfg->body.bf_sound_args.status; break; case SET_GET_BF_PERIODICITY: memcpy_ext(pmadapter, txbf->body.bf_periodicity->peer_mac, txbfcfg->body.bf_periodicity.peer_mac, MLAN_MAC_ADDR_LENGTH, MLAN_MAC_ADDR_LENGTH); txbf->body.bf_periodicity->interval = wlan_le16_to_cpu( txbfcfg->body.bf_periodicity.interval); break; case TX_BF_FOR_PEER_ENBL: txbf->no_of_peers = *(t_u8 *)&txbfcfg->body; tx_bf_peer = (bf_peer_args *)((t_u8 *)&txbfcfg->body + sizeof(t_u8)); for (i = 0; i < (int)txbf->no_of_peers; i++) { memcpy_ext(pmadapter, txbf->body.tx_bf_peer[i].peer_mac, (t_u8 *)tx_bf_peer->peer_mac, MLAN_MAC_ADDR_LENGTH, MLAN_MAC_ADDR_LENGTH); txbf->body.tx_bf_peer[i].bf_enbl = tx_bf_peer->bf_enbl; txbf->body.tx_bf_peer[i].sounding_enbl = tx_bf_peer->sounding_enbl; txbf->body.tx_bf_peer[i].fb_type = tx_bf_peer->fb_type; tx_bf_peer++; } break; case SET_SNR_THR_PEER: txbf->no_of_peers = *(t_u8 *)&txbfcfg->body; bf_snr = (bf_snr_thr_t *)((t_u8 *)&txbfcfg->body + sizeof(t_u8)); for (i = 0; i < (int)txbf->no_of_peers; i++) { memcpy_ext(pmadapter, txbf->body.bf_snr[i].peer_mac, (t_u8 *)bf_snr->peer_mac, MLAN_MAC_ADDR_LENGTH, MLAN_MAC_ADDR_LENGTH); txbf->body.bf_snr[i].snr = bf_snr->snr; bf_snr++; } break; default: LEAVE(); return MLAN_STATUS_FAILURE; } } LEAVE(); return MLAN_STATUS_SUCCESS; } /** * @brief Get second channel offset * * @param chan channel num * @return second channel offset */ t_u8 wlan_get_second_channel_offset(mlan_private *priv, int chan) { t_u8 chan2Offset = SEC_CHAN_NONE; /* Special Case: 20Mhz-only Channel */ if (priv->adapter->region_code != COUNTRY_CODE_US && chan == 165) return chan2Offset; switch (chan) { case 36: case 44: case 52: case 60: case 100: case 108: case 116: case 124: case 132: case 140: case 149: case 157: case 165: case 173: chan2Offset = SEC_CHAN_ABOVE; break; case 40: case 48: case 56: case 64: case 104: case 112: case 120: case 128: case 136: case 144: case 153: case 161: case 169: case 177: chan2Offset = SEC_CHAN_BELOW; break; } return chan2Offset; } #ifdef STA_SUPPORT /** * @brief validate the channel offset for Infra/Ad-hoc band configuration * * @param pmpriv A pointer to mlan_private structure * @param band band * @param chan primary channel * @param chan_bw channel bandwidth * * @return channel offset (NO_SEC_CHANNEL, SEC_CHANNEL_ABOVE, * SEC_CHANNEL_BELOW) */ t_u8 wlan_validate_chan_offset(mlan_private *pmpriv, t_u16 band, t_u32 chan, t_u8 chan_bw) { t_u8 chan_offset; pmlan_adapter pmadapter = pmpriv->adapter; if (chan_bw == CHANNEL_BW_40MHZ_ABOVE) chan_offset = SEC_CHAN_ABOVE; else if (chan_bw == CHANNEL_BW_40MHZ_BELOW) chan_offset = SEC_CHAN_BELOW; else chan_offset = SEC_CHAN_NONE; /* validation */ if (chan_offset != SEC_CHAN_NONE) { if (band & BAND_GN) { if ((chan == 1) || (chan == 2) || (chan == 3) || (chan == 4)) chan_offset = SEC_CHAN_ABOVE; else if ((chan == 10) || (chan == 11) || (chan == 12) || (chan == 13)) chan_offset = SEC_CHAN_BELOW; /* check if channel 12 is supported in the region */ if (!wlan_find_cfp_by_band_and_channel(pmadapter, band, 12)) if ((chan == 8) || (chan == 9)) chan_offset = SEC_CHAN_BELOW; } else if (band & BAND_AN) chan_offset = wlan_get_second_channel_offset(pmpriv, chan); } return chan_offset; } /** * @brief This function check if ht40 is allowed in current region * * @param pmpriv A pointer to mlan_private structure * @param pbss_desc A pointer to BSSDescriptor_t structure * * @return MTRUE/MFALSE */ static int wlan_check_chan_width_ht40_by_region(mlan_private *pmpriv, BSSDescriptor_t *pbss_desc) { pmlan_adapter pmadapter = pmpriv->adapter; int i = 0; int cover_pri_chan = MFALSE; t_u8 pri_chan; t_u8 chan_offset; t_u8 num_cfp; ENTER(); if (pbss_desc->pht_info == MNULL) { PRINTM(MERROR, "ht_info pointer NULL, force use HT20\n"); LEAVE(); return MFALSE; } if (pmpriv->curr_chan_flags & CHAN_FLAGS_NO_HT40PLUS && pmpriv->curr_chan_flags & CHAN_FLAGS_NO_HT40MINUS) { LEAVE(); return MFALSE; } pri_chan = pbss_desc->pht_info->ht_info.pri_chan; chan_offset = GET_SECONDARYCHAN(pbss_desc->pht_info->ht_info.field2); if ((chan_offset == SEC_CHAN_ABOVE) && (pmpriv->curr_chan_flags & CHAN_FLAGS_NO_HT40PLUS)) { pmpriv->curr_chan_flags |= CHAN_FLAGS_NO_HT40MINUS | CHAN_FLAGS_NO_80MHZ; return MFALSE; } if ((chan_offset == SEC_CHAN_BELOW) && (pmpriv->curr_chan_flags & CHAN_FLAGS_NO_HT40MINUS)) { pmpriv->curr_chan_flags |= CHAN_FLAGS_NO_HT40PLUS | CHAN_FLAGS_NO_80MHZ; return MFALSE; } if (pmpriv->curr_chan_flags & CHAN_FLAGS_MAX) return MTRUE; num_cfp = pmadapter->region_channel[0].num_cfp; if ((pbss_desc->bss_band & (BAND_B | BAND_G)) && pmadapter->region_channel[0].valid) { for (i = 0; i < num_cfp; i++) { if (pri_chan == pmadapter->region_channel[0].pcfp[i].channel) { cover_pri_chan = MTRUE; break; } } if (!cover_pri_chan) { PRINTM(MERROR, "Invalid channel, force use HT20\n"); LEAVE(); return MFALSE; } if (chan_offset == SEC_CHAN_ABOVE) { if (pri_chan > num_cfp - 4) { PRINTM(MERROR, "Invalid second channel offset, force use HT20\n"); LEAVE(); return MFALSE; } } } LEAVE(); return MTRUE; } /** * @brief This function append the 802_11N tlv * * @param pmpriv A pointer to mlan_private structure * @param pbss_desc A pointer to BSSDescriptor_t structure * @param ppbuffer A Pointer to command buffer pointer * * @return bytes added to the buffer */ int wlan_cmd_append_11n_tlv(mlan_private *pmpriv, BSSDescriptor_t *pbss_desc, t_u8 **ppbuffer) { pmlan_adapter pmadapter = pmpriv->adapter; MrvlIETypes_HTCap_t *pht_cap; MrvlIEtypes_ChanListParamSet_t *pchan_list; MrvlIETypes_2040BSSCo_t *p2040_bss_co; MrvlIETypes_ExtCap_t *pext_cap; t_u32 usr_dot_11n_dev_cap, orig_usr_dot_11n_dev_cap = 0; t_u8 usr_dot_11ac_bw; int ret_len = 0; ENTER(); /* Null Checks */ if (ppbuffer == MNULL) { LEAVE(); return 0; } if (*ppbuffer == MNULL) { LEAVE(); return 0; } if (pbss_desc == MNULL) { LEAVE(); return 0; } if (pbss_desc->bss_band & BAND_A) usr_dot_11n_dev_cap = pmpriv->usr_dot_11n_dev_cap_a; else usr_dot_11n_dev_cap = pmpriv->usr_dot_11n_dev_cap_bg; if (pmpriv->bss_mode == MLAN_BSS_MODE_IBSS) usr_dot_11ac_bw = BW_FOLLOW_VHTCAP; else usr_dot_11ac_bw = pmpriv->usr_dot_11ac_bw; if ((pbss_desc->bss_band & (BAND_B | BAND_G | BAND_A)) && ISSUPP_CHANWIDTH40(usr_dot_11n_dev_cap) && !wlan_check_chan_width_ht40_by_region(pmpriv, pbss_desc)) { orig_usr_dot_11n_dev_cap = usr_dot_11n_dev_cap; RESETSUPP_CHANWIDTH40(usr_dot_11n_dev_cap); RESET_40MHZ_INTOLARENT(usr_dot_11n_dev_cap); RESETSUPP_SHORTGI40(usr_dot_11n_dev_cap); pmpriv->usr_dot_11n_dev_cap_bg = usr_dot_11n_dev_cap; pbss_desc->curr_bandwidth = BW_20MHZ; } if (pbss_desc->pht_cap) { pht_cap = (MrvlIETypes_HTCap_t *)*ppbuffer; memset(pmadapter, pht_cap, 0, sizeof(MrvlIETypes_HTCap_t)); pht_cap->header.type = wlan_cpu_to_le16(HT_CAPABILITY); pht_cap->header.len = sizeof(HTCap_t); memcpy_ext(pmadapter, (t_u8 *)pht_cap + sizeof(MrvlIEtypesHeader_t), (t_u8 *)pbss_desc->pht_cap + sizeof(IEEEtypes_Header_t), pht_cap->header.len, pht_cap->header.len); pht_cap->ht_cap.ht_cap_info = wlan_le16_to_cpu(pht_cap->ht_cap.ht_cap_info); pht_cap->ht_cap.ht_ext_cap = wlan_le16_to_cpu(pht_cap->ht_cap.ht_ext_cap); wlan_fill_ht_cap_tlv(pmpriv, pht_cap, pbss_desc->bss_band, MTRUE); /** check if need support 80+80MHZ */ /** reset the 2 spatial stream rate for 80 + 80 Mhz */ if (wlan_is_80_80_support(pmpriv, pbss_desc)) pht_cap->ht_cap.supported_mcs_set[1] = 0; HEXDUMP("HT_CAPABILITIES IE", (t_u8 *)pht_cap, sizeof(MrvlIETypes_HTCap_t)); *ppbuffer += sizeof(MrvlIETypes_HTCap_t); ret_len += sizeof(MrvlIETypes_HTCap_t); pht_cap->header.len = wlan_cpu_to_le16(pht_cap->header.len); } else { // AP don't support 11N LEAVE(); return 0; } if (pbss_desc->pht_info) { pchan_list = (MrvlIEtypes_ChanListParamSet_t *)*ppbuffer; memset(pmadapter, pchan_list, 0, sizeof(MrvlIEtypes_ChanListParamSet_t)); pchan_list->header.type = wlan_cpu_to_le16(TLV_TYPE_CHANLIST); pchan_list->header.len = sizeof(MrvlIEtypes_ChanListParamSet_t) - sizeof(MrvlIEtypesHeader_t); pchan_list->chan_scan_param[0].chan_number = pbss_desc->pht_info->ht_info.pri_chan; pchan_list->chan_scan_param[0].bandcfg.chanBand = wlan_band_to_radio_type(pbss_desc->bss_band); /* support the VHT if the network to be join has the VHT * operation */ if (ISSUPP_11ACENABLED(pmadapter->fw_cap_info) && (usr_dot_11ac_bw == BW_FOLLOW_VHTCAP) && (!(pmpriv->curr_chan_flags & CHAN_FLAGS_NO_80MHZ)) && wlan_11ac_bandconfig_allowed(pmpriv, pbss_desc->bss_band) && pbss_desc->pvht_oprat && pbss_desc->pvht_oprat->chan_width == VHT_OPER_CHWD_80MHZ) { pchan_list->chan_scan_param[0].bandcfg.chanWidth = CHAN_BW_80MHZ; pchan_list->chan_scan_param[0].bandcfg.chan2Offset = GET_SECONDARYCHAN( pbss_desc->pht_info->ht_info.field2); pbss_desc->curr_bandwidth = BW_80MHZ; } else if (ISSUPP_CHANWIDTH40(usr_dot_11n_dev_cap) && ISALLOWED_CHANWIDTH40( pbss_desc->pht_info->ht_info.field2) && wlan_check_chan_width_ht40_by_region(pmpriv, pbss_desc)) { pchan_list->chan_scan_param[0].bandcfg.chan2Offset = GET_SECONDARYCHAN( pbss_desc->pht_info->ht_info.field2); pbss_desc->curr_bandwidth = BW_40MHZ; pchan_list->chan_scan_param[0].bandcfg.chanWidth = CHAN_BW_40MHZ; } pchan_list->chan_scan_param[0].bandcfg.scanMode = SCAN_MODE_USER; HEXDUMP("ChanList", (t_u8 *)pchan_list, sizeof(MrvlIEtypes_ChanListParamSet_t)); HEXDUMP("pht_info", (t_u8 *)pbss_desc->pht_info, sizeof(MrvlIETypes_HTInfo_t) - 2); *ppbuffer += sizeof(MrvlIEtypes_ChanListParamSet_t); ret_len += sizeof(MrvlIEtypes_ChanListParamSet_t); pchan_list->header.len = wlan_cpu_to_le16(pchan_list->header.len); } if (pbss_desc->pbss_co_2040) { p2040_bss_co = (MrvlIETypes_2040BSSCo_t *)*ppbuffer; memset(pmadapter, p2040_bss_co, 0, sizeof(MrvlIETypes_2040BSSCo_t)); p2040_bss_co->header.type = wlan_cpu_to_le16(BSSCO_2040); p2040_bss_co->header.len = sizeof(BSSCo2040_t); memcpy_ext(pmadapter, (t_u8 *)p2040_bss_co + sizeof(MrvlIEtypesHeader_t), (t_u8 *)pbss_desc->pbss_co_2040 + sizeof(IEEEtypes_Header_t), p2040_bss_co->header.len, p2040_bss_co->header.len); HEXDUMP("20/40 BSS Coexistence IE", (t_u8 *)p2040_bss_co, sizeof(MrvlIETypes_2040BSSCo_t)); *ppbuffer += sizeof(MrvlIETypes_2040BSSCo_t); ret_len += sizeof(MrvlIETypes_2040BSSCo_t); p2040_bss_co->header.len = wlan_cpu_to_le16(p2040_bss_co->header.len); } if (pbss_desc->pext_cap) { pext_cap = (MrvlIETypes_ExtCap_t *)*ppbuffer; memset(pmadapter, pext_cap, 0, sizeof(MrvlIETypes_ExtCap_t)); pext_cap->header.type = wlan_cpu_to_le16(EXT_CAPABILITY); pext_cap->header.len = sizeof(ExtCap_t); memcpy_ext(pmadapter, (t_u8 *)pext_cap + sizeof(MrvlIEtypesHeader_t), (t_u8 *)&pmpriv->ext_cap, sizeof(ExtCap_t), pext_cap->header.len); if (pbss_desc && pbss_desc->multi_bssid_ap) SET_EXTCAP_MULTI_BSSID(pext_cap->ext_cap); if (!pmadapter->ecsa_enable) RESET_EXTCAP_EXT_CHANNEL_SWITCH(pext_cap->ext_cap); else SET_EXTCAP_EXT_CHANNEL_SWITCH(pext_cap->ext_cap); HEXDUMP("Extended Capabilities IE", (t_u8 *)pext_cap, sizeof(MrvlIETypes_ExtCap_t)); *ppbuffer += sizeof(MrvlIETypes_ExtCap_t); ret_len += sizeof(MrvlIETypes_ExtCap_t); pext_cap->header.len = wlan_cpu_to_le16(pext_cap->header.len); } else if (wlan_is_ext_capa_support(pmpriv) || (pmpriv->config_bands & BAND_AAC)) { wlan_add_ext_capa_info_ie(pmpriv, pbss_desc, ppbuffer); ret_len += sizeof(MrvlIETypes_ExtCap_t); } PRINTM(MCMND, "curr_bandwidth=%d\n", pbss_desc->curr_bandwidth); if (orig_usr_dot_11n_dev_cap) pmpriv->usr_dot_11n_dev_cap_bg = orig_usr_dot_11n_dev_cap; LEAVE(); return ret_len; } #endif /* STA_SUPPORT */ /** * @brief 11n configuration handler * * @param pmadapter A pointer to mlan_adapter structure * @param pioctl_req A pointer to ioctl request buffer * * @return MLAN_STATUS_SUCCESS --success, otherwise fail */ mlan_status wlan_11n_cfg_ioctl(pmlan_adapter pmadapter, pmlan_ioctl_req pioctl_req) { mlan_status status = MLAN_STATUS_SUCCESS; mlan_ds_11n_cfg *cfg = MNULL; ENTER(); if (pioctl_req->buf_len < sizeof(mlan_ds_11n_cfg)) { PRINTM(MINFO, "MLAN bss IOCTL length is too short.\n"); pioctl_req->data_read_written = 0; pioctl_req->buf_len_needed = sizeof(mlan_ds_11n_cfg); pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; LEAVE(); return MLAN_STATUS_RESOURCE; } cfg = (mlan_ds_11n_cfg *)pioctl_req->pbuf; switch (cfg->sub_command) { case MLAN_OID_11N_CFG_TX: status = wlan_11n_ioctl_httxcfg(pmadapter, pioctl_req); break; case MLAN_OID_11N_HTCAP_CFG: status = wlan_11n_ioctl_htusrcfg(pmadapter, pioctl_req); break; case MLAN_OID_11N_CFG_AGGR_PRIO_TBL: status = wlan_11n_ioctl_aggr_prio_tbl(pmadapter, pioctl_req); break; case MLAN_OID_11N_CFG_ADDBA_REJECT: status = wlan_11n_ioctl_addba_reject(pmadapter, pioctl_req); break; case MLAN_OID_11N_CFG_ADDBA_PARAM: status = wlan_11n_ioctl_addba_param(pmadapter, pioctl_req); break; case MLAN_OID_11N_CFG_DELBA: status = wlan_11n_ioctl_delba(pmadapter, pioctl_req); break; case MLAN_OID_11N_CFG_REJECT_ADDBA_REQ: status = wlan_11n_ioctl_rejectaddbareq(pmadapter, pioctl_req); break; case MLAN_OID_11N_CFG_MAX_TX_BUF_SIZE: status = wlan_11n_ioctl_max_tx_buf_size(pmadapter, pioctl_req); break; case MLAN_OID_11N_CFG_AMSDU_AGGR_CTRL: status = wlan_11n_ioctl_amsdu_aggr_ctrl(pmadapter, pioctl_req); break; case MLAN_OID_11N_CFG_SUPPORTED_MCS_SET: status = wlan_11n_ioctl_supported_mcs_set(pmadapter, pioctl_req); break; case MLAN_OID_11N_CFG_TX_BF_CAP: status = wlan_11n_ioctl_tx_bf_cap(pmadapter, pioctl_req); break; case MLAN_OID_11N_CFG_TX_BF_CFG: status = wlan_11n_ioctl_tx_bf_cfg(pmadapter, pioctl_req); break; case MLAN_OID_11N_CFG_STREAM_CFG: status = wlan_11n_ioctl_stream_cfg(pmadapter, pioctl_req); break; case MLAN_OID_11N_CFG_COEX_RX_WINSIZE: status = wlan_11n_ioctl_coex_rx_winsize(pmadapter, pioctl_req); break; case MLAN_OID_11N_CFG_TX_AGGR_CTRL: status = wlan_11n_ioctl_txaggrctrl(pmadapter, pioctl_req); break; case MLAN_OID_11N_CFG_IBSS_AMPDU_PARAM: status = wlan_11n_ioctl_ibss_ampdu_param(pmadapter, pioctl_req); break; case MLAN_OID_11N_CFG_MIN_BA_THRESHOLD: status = wlan_11n_ioctl_min_ba_threshold_cfg(pmadapter, pioctl_req); break; default: pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID; status = MLAN_STATUS_FAILURE; break; } LEAVE(); return status; } /** * @brief This function will delete the given entry in Tx BA Stream table * * @param priv Pointer to mlan_private * @param ptx_tbl Pointer to tx ba stream entry to delete * * @return N/A */ void wlan_11n_delete_txbastream_tbl_entry(mlan_private *priv, TxBAStreamTbl *ptx_tbl) { pmlan_adapter pmadapter = priv->adapter; ENTER(); if (!ptx_tbl || !wlan_is_txbastreamptr_valid(priv, ptx_tbl)) goto exit; PRINTM(MINFO, "Delete BA stream table entry: %p\n", ptx_tbl); util_unlink_list(pmadapter->pmoal_handle, &priv->tx_ba_stream_tbl_ptr, (pmlan_linked_list)ptx_tbl, MNULL, MNULL); pmadapter->callbacks.moal_mfree(pmadapter->pmoal_handle, (t_u8 *)ptx_tbl); exit: LEAVE(); } /** * @brief This function will delete all the entries in Tx BA Stream table * * @param priv A pointer to mlan_private * * @return N/A */ void wlan_11n_deleteall_txbastream_tbl(mlan_private *priv) { int i; TxBAStreamTbl *del_tbl_ptr = MNULL; ENTER(); wlan_request_ralist_lock(priv); while ((del_tbl_ptr = (TxBAStreamTbl *)util_peek_list( priv->adapter->pmoal_handle, &priv->tx_ba_stream_tbl_ptr, MNULL, MNULL))) { wlan_11n_delete_txbastream_tbl_entry(priv, del_tbl_ptr); } util_init_list((pmlan_linked_list)&priv->tx_ba_stream_tbl_ptr); wlan_release_ralist_lock(priv); for (i = 0; i < MAX_NUM_TID; ++i) { priv->aggr_prio_tbl[i].ampdu_ap = priv->aggr_prio_tbl[i].ampdu_user; } LEAVE(); } /** * @brief This function will return the pointer to an entry in BA Stream * table which matches the give RA/TID pair * * @param priv A pointer to mlan_private * @param tid TID to find in reordering table * @param ra RA to find in reordering table * @param lock flag for request the spin_lock * * @return A pointer to first entry matching RA/TID in BA stream * NULL if not found */ TxBAStreamTbl *wlan_11n_get_txbastream_tbl(mlan_private *priv, int tid, t_u8 *ra, int lock) { TxBAStreamTbl *ptx_tbl; pmlan_adapter pmadapter = priv->adapter; ENTER(); if (lock) wlan_request_ralist_lock(priv); ptx_tbl = (TxBAStreamTbl *)util_peek_list(pmadapter->pmoal_handle, &priv->tx_ba_stream_tbl_ptr, MNULL, MNULL); if (!ptx_tbl) { if (lock) wlan_release_ralist_lock(priv); LEAVE(); return MNULL; } while (ptx_tbl != (TxBAStreamTbl *)&priv->tx_ba_stream_tbl_ptr) { PRINTM(MDAT_D, "get_txbastream_tbl TID %d\n", ptx_tbl->tid); DBG_HEXDUMP(MDAT_D, "RA", ptx_tbl->ra, MLAN_MAC_ADDR_LENGTH); if ((!memcmp(pmadapter, ptx_tbl->ra, ra, MLAN_MAC_ADDR_LENGTH)) && (ptx_tbl->tid == tid)) { if (lock) wlan_release_ralist_lock(priv); LEAVE(); return ptx_tbl; } ptx_tbl = ptx_tbl->pnext; } if (lock) wlan_release_ralist_lock(priv); LEAVE(); return MNULL; } /** * @brief This function will set ba_status txbastream entry with * given RA/TID. * * @param priv A pointer to mlan_private * @param ra RA to find in txbastream * @param tid TID to find in txbastream * @param ba_status BA stream status * * @return N/A */ void wlan_11n_set_txbastream_status(mlan_private *priv, int tid, t_u8 *ra, baStatus_e ba_status, int lock) { TxBAStreamTbl *ptx_tbl; pmlan_adapter pmadapter = priv->adapter; ENTER(); if (lock) wlan_request_ralist_lock(priv); ptx_tbl = (TxBAStreamTbl *)util_peek_list(pmadapter->pmoal_handle, &priv->tx_ba_stream_tbl_ptr, MNULL, MNULL); if (!ptx_tbl) { if (lock) wlan_release_ralist_lock(priv); LEAVE(); return; } while (ptx_tbl != (TxBAStreamTbl *)&priv->tx_ba_stream_tbl_ptr) { PRINTM(MDAT_D, "get_txbastream_tbl TID %d\n", ptx_tbl->tid); DBG_HEXDUMP(MDAT_D, "RA", ptx_tbl->ra, MLAN_MAC_ADDR_LENGTH); if ((!memcmp(pmadapter, ptx_tbl->ra, ra, MLAN_MAC_ADDR_LENGTH)) && (ptx_tbl->tid == tid)) { ptx_tbl->ba_status = ba_status; if (lock) wlan_release_ralist_lock(priv); LEAVE(); return; } ptx_tbl = ptx_tbl->pnext; } if (lock) wlan_release_ralist_lock(priv); LEAVE(); return; } /** * @brief This function will create a entry in tx ba stream table for the * given RA/TID. * * @param priv A pointer to mlan_private * @param ra RA to find in reordering table * @param tid TID to find in reordering table * @param ba_status BA stream status to create the stream with * * @return N/A */ void wlan_11n_create_txbastream_tbl(mlan_private *priv, t_u8 *ra, int tid, baStatus_e ba_status) { TxBAStreamTbl *new_node = MNULL; pmlan_adapter pmadapter = priv->adapter; raListTbl *ra_list = MNULL; int tid_down; ENTER(); PRINTM(MDAT_D, "create_txbastream_tbl TID %d\n", tid); DBG_HEXDUMP(MDAT_D, "RA", ra, MLAN_MAC_ADDR_LENGTH); if (pmadapter->callbacks.moal_malloc( pmadapter->pmoal_handle, sizeof(TxBAStreamTbl), MLAN_MEM_DEF | MLAN_MEM_FLAG_ATOMIC, (t_u8 **)&new_node)) { PRINTM(MERROR, "wlan_11n_create_txbastream_tbl Failed to allocate new_node\n"); LEAVE(); return; } tid_down = wlan_get_wmm_tid_down(priv, tid); ra_list = wlan_wmm_get_ralist_node(priv, tid_down, ra); if (ra_list) { ra_list->amsdu_in_ampdu = MFALSE; ra_list->ba_status = ba_status; } util_init_list((pmlan_linked_list)new_node); new_node->tid = tid; new_node->ba_status = ba_status; memcpy_ext(pmadapter, new_node->ra, ra, MLAN_MAC_ADDR_LENGTH, MLAN_MAC_ADDR_LENGTH); util_enqueue_list_tail(pmadapter->pmoal_handle, &priv->tx_ba_stream_tbl_ptr, (pmlan_linked_list)new_node, MNULL, MNULL); LEAVE(); } /** * @brief This function will send a block ack to given tid/ra * * @param priv A pointer to mlan_private * @param tid TID to send the ADDBA * @param peer_mac MAC address to send the ADDBA * * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE */ int wlan_send_addba(mlan_private *priv, int tid, t_u8 *peer_mac) { HostCmd_DS_11N_ADDBA_REQ add_ba_req; static t_u8 dialog_tok; mlan_status ret; ENTER(); PRINTM(MCMND, "Send addba: TID %d, " MACSTR "\n", tid, MAC2STR(peer_mac)); add_ba_req.block_ack_param_set = (t_u16)( (tid << BLOCKACKPARAM_TID_POS) | (priv->add_ba_param.tx_win_size << BLOCKACKPARAM_WINSIZE_POS) | IMMEDIATE_BLOCK_ACK); /** enable AMSDU inside AMPDU */ if (priv->add_ba_param.tx_amsdu && (priv->aggr_prio_tbl[tid].amsdu != BA_STREAM_NOT_ALLOWED)) add_ba_req.block_ack_param_set |= BLOCKACKPARAM_AMSDU_SUPP_MASK; add_ba_req.block_ack_tmo = (t_u16)priv->add_ba_param.timeout; ++dialog_tok; if (dialog_tok == 0) dialog_tok = 1; add_ba_req.dialog_token = dialog_tok; memcpy_ext(priv->adapter, &add_ba_req.peer_mac_addr, peer_mac, MLAN_MAC_ADDR_LENGTH, MLAN_MAC_ADDR_LENGTH); /* We don't wait for the response of this command */ ret = wlan_prepare_cmd(priv, HostCmd_CMD_11N_ADDBA_REQ, 0, 0, MNULL, &add_ba_req); LEAVE(); return ret; } /** * @brief This function will delete a block ack to given tid/ra * * @param priv A pointer to mlan_private * @param pioctl_req A pointer to ioctl request buffer * @param tid TID to send the ADDBA * @param peer_mac MAC address to send the ADDBA * @param initiator MTRUE if we have initiated ADDBA, MFALSE otherwise * * @return MLAN_STATUS_PENDING --success, otherwise fail */ int wlan_send_delba(mlan_private *priv, pmlan_ioctl_req pioctl_req, int tid, t_u8 *peer_mac, int initiator) { HostCmd_DS_11N_DELBA delba; mlan_status ret; ENTER(); PRINTM(MCMND, "Send delba: TID %d initiator=%d, " MACSTR "\n", tid, initiator, MAC2STR(peer_mac)); memset(priv->adapter, &delba, 0, sizeof(delba)); delba.del_ba_param_set = (tid << DELBA_TID_POS); if (initiator) DELBA_INITIATOR(delba.del_ba_param_set); else DELBA_RECIPIENT(delba.del_ba_param_set); memcpy_ext(priv->adapter, &delba.peer_mac_addr, peer_mac, MLAN_MAC_ADDR_LENGTH, MLAN_MAC_ADDR_LENGTH); ret = wlan_prepare_cmd(priv, HostCmd_CMD_11N_DELBA, HostCmd_ACT_GEN_SET, 0, (t_void *)pioctl_req, (t_void *)&delba); if (ret == MLAN_STATUS_SUCCESS) ret = MLAN_STATUS_PENDING; LEAVE(); return ret; } /** * @brief This function handles the command response of * delete a block ack request * * @param priv A pointer to mlan_private structure * @param del_ba A pointer to command response buffer * * @return N/A */ void wlan_11n_delete_bastream(mlan_private *priv, t_u8 *del_ba) { HostCmd_DS_11N_DELBA *pdel_ba = (HostCmd_DS_11N_DELBA *)del_ba; int tid; ENTER(); DBG_HEXDUMP(MCMD_D, "Delba:", (t_u8 *)pdel_ba, 20); pdel_ba->del_ba_param_set = wlan_le16_to_cpu(pdel_ba->del_ba_param_set); pdel_ba->reason_code = wlan_le16_to_cpu(pdel_ba->reason_code); tid = pdel_ba->del_ba_param_set >> DELBA_TID_POS; mlan_11n_delete_bastream_tbl(priv, tid, pdel_ba->peer_mac_addr, TYPE_DELBA_RECEIVE, INITIATOR_BIT(pdel_ba->del_ba_param_set), pdel_ba->reason_code); LEAVE(); } /** * @brief Get Rx reordering table * * @param priv A pointer to mlan_private structure * @param buf A pointer to rx_reorder_tbl structure * @return number of rx reorder table entry */ int wlan_get_rxreorder_tbl(mlan_private *priv, rx_reorder_tbl *buf) { int i; rx_reorder_tbl *ptbl = buf; RxReorderTbl *rx_reorder_tbl_ptr; int count = 0; ENTER(); priv->adapter->callbacks.moal_spin_lock(priv->adapter->pmoal_handle, priv->rx_reorder_tbl_ptr.plock); rx_reorder_tbl_ptr = (RxReorderTbl *)util_peek_list(priv->adapter->pmoal_handle, &priv->rx_reorder_tbl_ptr, MNULL, MNULL); if (!rx_reorder_tbl_ptr) { priv->adapter->callbacks.moal_spin_unlock( priv->adapter->pmoal_handle, priv->rx_reorder_tbl_ptr.plock); LEAVE(); return count; } while (rx_reorder_tbl_ptr != (RxReorderTbl *)&priv->rx_reorder_tbl_ptr) { ptbl->tid = (t_u16)rx_reorder_tbl_ptr->tid; memcpy_ext(priv->adapter, ptbl->ta, rx_reorder_tbl_ptr->ta, MLAN_MAC_ADDR_LENGTH, MLAN_MAC_ADDR_LENGTH); ptbl->start_win = rx_reorder_tbl_ptr->start_win; ptbl->win_size = rx_reorder_tbl_ptr->win_size; ptbl->amsdu = rx_reorder_tbl_ptr->amsdu; for (i = 0; i < rx_reorder_tbl_ptr->win_size; ++i) { if (rx_reorder_tbl_ptr->rx_reorder_ptr[i]) ptbl->buffer[i] = MTRUE; else ptbl->buffer[i] = MFALSE; } rx_reorder_tbl_ptr = rx_reorder_tbl_ptr->pnext; ptbl++; count++; if (count >= MLAN_MAX_RX_BASTREAM_SUPPORTED) break; } priv->adapter->callbacks.moal_spin_unlock( priv->adapter->pmoal_handle, priv->rx_reorder_tbl_ptr.plock); LEAVE(); return count; } /** * @brief Get transmit BA stream table * * @param priv A pointer to mlan_private structure * @param buf A pointer to tx_ba_stream_tbl structure * @return number of ba stream table entry */ int wlan_get_txbastream_tbl(mlan_private *priv, tx_ba_stream_tbl *buf) { TxBAStreamTbl *ptxtbl; tx_ba_stream_tbl *ptbl = buf; int count = 0; t_u32 bastream_max = 0; ENTER(); wlan_request_ralist_lock(priv); ptxtbl = (TxBAStreamTbl *)util_peek_list(priv->adapter->pmoal_handle, &priv->tx_ba_stream_tbl_ptr, MNULL, MNULL); if (!ptxtbl) { wlan_release_ralist_lock(priv); LEAVE(); return count; } bastream_max = ISSUPP_GETTXBASTREAM(priv->adapter->hw_dot_11n_dev_cap); if (bastream_max == 0) bastream_max = MLAN_MAX_TX_BASTREAM_DEFAULT; while (ptxtbl != (TxBAStreamTbl *)&priv->tx_ba_stream_tbl_ptr) { ptbl->tid = (t_u16)ptxtbl->tid; PRINTM(MINFO, "tid=%d\n", ptbl->tid); memcpy_ext(priv->adapter, ptbl->ra, ptxtbl->ra, MLAN_MAC_ADDR_LENGTH, MLAN_MAC_ADDR_LENGTH); ptbl->amsdu = ptxtbl->amsdu; ptxtbl = ptxtbl->pnext; ptbl++; count++; if (count >= (int)bastream_max) break; } wlan_release_ralist_lock(priv); LEAVE(); return count; } /** * @brief This function check if 11AC is allowed in bandcfg * * @param pmpriv A pointer to mlan_private structure * @param bss_band bss band * * @return 0--not allowed, other value allowed */ t_u8 wlan_11n_bandconfig_allowed(mlan_private *pmpriv, t_u16 bss_band) { if (pmpriv->bss_mode == MLAN_BSS_MODE_IBSS) { if (bss_band & BAND_G) return (pmpriv->adapter->adhoc_start_band & BAND_GN); else if (bss_band & BAND_A) return (pmpriv->adapter->adhoc_start_band & BAND_AN); } else { if (bss_band & BAND_G) return (pmpriv->config_bands & BAND_GN); else if (bss_band & BAND_A) return (pmpriv->config_bands & BAND_AN); } return 0; } /** * @brief This function cleans up txbastream_tbl for specific station * * @param priv A pointer to mlan_private * @param ra RA to find in txbastream_tbl * @return N/A */ void wlan_11n_cleanup_txbastream_tbl(mlan_private *priv, t_u8 *ra) { TxBAStreamTbl *ptx_tbl = MNULL; t_u8 i; ENTER(); wlan_request_ralist_lock(priv); for (i = 0; i < MAX_NUM_TID; ++i) { ptx_tbl = wlan_11n_get_txbastream_tbl(priv, i, ra, MFALSE); if (ptx_tbl) wlan_11n_delete_txbastream_tbl_entry(priv, ptx_tbl); } wlan_release_ralist_lock(priv); LEAVE(); return; } void wlan_update_11n_cap(mlan_private *pmpriv) { mlan_adapter *pmadapter = pmpriv->adapter; pmpriv->usr_dev_mcs_support = pmadapter->hw_dev_mcs_support; pmpriv->usr_dot_11n_dev_cap_bg = pmadapter->hw_dot_11n_dev_cap & DEFAULT_11N_CAP_MASK_BG; pmpriv->usr_dot_11n_dev_cap_a = pmadapter->hw_dot_11n_dev_cap & DEFAULT_11N_CAP_MASK_A; }