diff options
Diffstat (limited to 'drivers/net/wireless/sd8797/mlan/mlan_11n.c')
-rw-r--r-- | drivers/net/wireless/sd8797/mlan/mlan_11n.c | 1937 |
1 files changed, 1937 insertions, 0 deletions
diff --git a/drivers/net/wireless/sd8797/mlan/mlan_11n.c b/drivers/net/wireless/sd8797/mlan/mlan_11n.c new file mode 100644 index 000000000000..d55c2e7e4ab3 --- /dev/null +++ b/drivers/net/wireless/sd8797/mlan/mlan_11n.c @@ -0,0 +1,1937 @@ +/** @file mlan_11n.c + * + * @brief This file contains functions for 11n handling. + * + * Copyright (C) 2008-2011, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +/******************************************************** +Change log: + 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" + +/******************************************************** + 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(IN pmlan_adapter pmadapter, + IN 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; + 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(IN pmlan_adapter pmadapter, + IN 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) { + pmadapter->usr_dot_11n_dev_cap_bg = cfg->param.htcap_cfg.htcap; + PRINTM(MINFO, "Set: UsrDot11nCap for 2.4GHz 0x%x\n", + pmadapter->usr_dot_11n_dev_cap_bg); + } + if (cfg->param.htcap_cfg.misc_cfg == BAND_SELECT_A) { + pmadapter->usr_dot_11n_dev_cap_a = cfg->param.htcap_cfg.htcap; + PRINTM(MINFO, "Set: UsrDot11nCap for 5GHz 0x%x\n", + pmadapter->usr_dot_11n_dev_cap_a); + } + if (cfg->param.htcap_cfg.misc_cfg == BAND_SELECT_BOTH) { + pmadapter->usr_dot_11n_dev_cap_bg = cfg->param.htcap_cfg.htcap; + pmadapter->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 = pmadapter->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 = pmadapter->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(IN pmlan_adapter pmadapter, + IN 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(IN pmlan_adapter pmadapter, + IN 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(IN pmlan_adapter pmadapter, + IN 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(IN pmlan_adapter pmadapter, + IN 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(IN pmlan_adapter pmadapter, + IN 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.stream_cfg = pmadapter->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 + pmadapter->usr_dev_mcs_support = cfg->param.stream_cfg; + break; + case HT_STREAM_MODE_1X1: + pmadapter->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 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(); + + if (! + (ptx_tbl = + (TxBAStreamTbl *) util_peek_list(priv->adapter->pmoal_handle, + &priv->tx_ba_stream_tbl_ptr, + priv->adapter->callbacks. + moal_spin_lock, + priv->adapter->callbacks. + moal_spin_unlock))) { + LEAVE(); + return; + } + + while (ptx_tbl != (TxBAStreamTbl *) & priv->tx_ba_stream_tbl_ptr) { + wlan_send_addba(priv, ptx_tbl->tid, ptx_tbl->ra); + ptx_tbl = ptx_tbl->pnext; + } + /* 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(IN pmlan_adapter pmadapter, + IN 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->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 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(IN pmlan_adapter pmadapter, + IN 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(pmadapter, cfg->param.addba_reject, pmpriv->addba_reject, + MAX_NUM_TID); + } else { + if (pmpriv->media_connected == MTRUE) { + PRINTM(MERROR, "Can not set aggr priority table in connected" + " state\n"); + pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID; + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + 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]; + } + } + + LEAVE(); + return ret; +} + +/** + * @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(IN pmlan_adapter pmadapter, + IN 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 { + if (pmpriv->media_connected == MTRUE) { + PRINTM(MERROR, "Can not set aggr priority table in connected" + " state\n"); + pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID; + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + 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]; + } + } + } + + LEAVE(); + return ret; +} + +/** + * @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(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_ds_11n_cfg *cfg = MNULL; + int rx_mcs_supp; + t_u8 mcs_set[NUM_MCS_FIELD]; + + 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(pmadapter->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(pmadapter->usr_dot_11n_dev_cap_bg) + || ISSUPP_CHANWIDTH40(pmadapter->usr_dot_11n_dev_cap_a) + ) + SETHT_MCS32(mcs_set); + + cfg = (mlan_ds_11n_cfg *) pioctl_req->pbuf; + memcpy(pmadapter, cfg->param.supported_mcs_set, mcs_set, 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(); + + if (! + (ptx_tbl = + (TxBAStreamTbl *) util_peek_list(priv->adapter->pmoal_handle, + &priv->tx_ba_stream_tbl_ptr, MNULL, + MNULL))) { + 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(); + + if (! + (ptx_tbl = + (TxBAStreamTbl *) util_peek_list(priv->adapter->pmoal_handle, + &priv->tx_ba_stream_tbl_ptr, + priv->adapter->callbacks. + moal_spin_lock, + priv->adapter->callbacks. + moal_spin_unlock))) { + 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, + MrvlIETypes_HTCap_t * pht_cap, t_u8 bands) +{ + mlan_adapter *pmadapter = priv->adapter; + t_u32 usr_dot_11n_dev_cap; + + ENTER(); + + if (bands & BAND_A) + usr_dot_11n_dev_cap = pmadapter->usr_dot_11n_dev_cap_a; + else + usr_dot_11n_dev_cap = pmadapter->usr_dot_11n_dev_cap_bg; + + if (ISSUPP_CHANWIDTH40(usr_dot_11n_dev_cap)) + SETHT_SUPPCHANWIDTH(pht_cap->ht_cap.ht_cap_info); + else + RESETHT_SUPPCHANWIDTH(pht_cap->ht_cap.ht_cap_info); + + if (ISSUPP_GREENFIELD(usr_dot_11n_dev_cap)) + SETHT_GREENFIELD(pht_cap->ht_cap.ht_cap_info); + else + RESETHT_GREENFIELD(pht_cap->ht_cap.ht_cap_info); + + if (ISSUPP_SHORTGI20(usr_dot_11n_dev_cap)) + SETHT_SHORTGI20(pht_cap->ht_cap.ht_cap_info); + else + RESETHT_SHORTGI20(pht_cap->ht_cap.ht_cap_info); + + if (ISSUPP_SHORTGI40(usr_dot_11n_dev_cap)) + SETHT_SHORTGI40(pht_cap->ht_cap.ht_cap_info); + else + RESETHT_SHORTGI40(pht_cap->ht_cap.ht_cap_info); + + if (ISSUPP_RXSTBC(usr_dot_11n_dev_cap)) + SETHT_RXSTBC(pht_cap->ht_cap.ht_cap_info, 1); + else + RESETHT_RXSTBC(pht_cap->ht_cap.ht_cap_info); + + if (ISENABLED_40MHZ_INTOLARENT(usr_dot_11n_dev_cap)) + SETHT_40MHZ_INTOLARANT(pht_cap->ht_cap.ht_cap_info); + else + RESETHT_40MHZ_INTOLARANT(pht_cap->ht_cap.ht_cap_info); + + /* No user config for LDPC coding capability yet */ + if (ISSUPP_RXLDPC(pmadapter->hw_dot_11n_dev_cap)) + SETHT_LDPCCODINGCAP(pht_cap->ht_cap.ht_cap_info); + else + RESETHT_LDPCCODINGCAP(pht_cap->ht_cap.ht_cap_info); + + /* No user config for TX STBC yet */ + if (ISSUPP_TXSTBC(pmadapter->hw_dot_11n_dev_cap)) + SETHT_TXSTBC(pht_cap->ht_cap.ht_cap_info); + else + RESETHT_TXSTBC(pht_cap->ht_cap.ht_cap_info); + + /* No user config for Delayed BACK yet */ + if (GET_DELAYEDBACK(pmadapter->hw_dot_11n_dev_cap)) + SETHT_DELAYEDBACK(pht_cap->ht_cap.ht_cap_info); + else + RESETHT_DELAYEDBACK(pht_cap->ht_cap.ht_cap_info); + + /* Need change to support 8k AMSDU receive */ + RESETHT_MAXAMSDU(pht_cap->ht_cap.ht_cap_info); + + 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 + * + * @return N/A + */ +void +wlan_fill_ht_cap_tlv(mlan_private * priv, + MrvlIETypes_HTCap_t * pht_cap, t_u8 bands) +{ + 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 = pmadapter->usr_dot_11n_dev_cap_a; + else + usr_dot_11n_dev_cap = pmadapter->usr_dot_11n_dev_cap_bg; + + /* Fill HT cap info */ + wlan_fill_cap_info(priv, pht_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, 0); + + rx_mcs_supp = GET_RXMCSSUPP(pmadapter->usr_dev_mcs_support); + /* Set MCS for 1x1/2x2 */ + memset(pmadapter, (t_u8 *) pht_cap->ht_cap.supported_mcs_set, 0xff, + rx_mcs_supp); + /* Clear all the other values */ + 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 (ISSUPP_CHANWIDTH40(usr_dot_11n_dev_cap)) + 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; +} +#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 Delayed Block Ack streams = %d\n", + GET_DELAYEDBACK(cap)); + PRINTM(MINFO, "GET_HW_SPEC: Number of Immediate Block Ack streams = %d\n", + GET_IMMEDIATEBACK(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)); + + if ((ptx_ba_tbl = wlan_11n_get_txbastream_status(priv, + BA_STREAM_SETUP_INPROGRESS))) + { + 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_11n_create_txbastream_tbl(priv, pdel_ba->peer_mac_addr, tid, + BA_STREAM_SETUP_INPROGRESS); + if ((ptx_ba_tbl = wlan_11n_get_txbastream_status(priv, + BA_STREAM_SETUP_INPROGRESS))) + { + mlan_11n_delete_bastream_tbl(priv, ptx_ba_tbl->tid, + ptx_ba_tbl->ra, TYPE_DELBA_SENT, + MTRUE); + } + } + } + + 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; + + 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; + if (padd_ba_rsp->status_code == BA_RESULT_SUCCESS) { + if ((ptx_ba_tbl = wlan_11n_get_txbastream_tbl(priv, tid, + padd_ba_rsp-> + peer_mac_addr))) { + PRINTM(MCMND, + "ADDBA REQ: %02x:%02x:%02x:%02x:%02x:%02x tid=%d ssn=%d win_size=%d,amsdu=%d\n", + padd_ba_rsp->peer_mac_addr[0], padd_ba_rsp->peer_mac_addr[1], + padd_ba_rsp->peer_mac_addr[2], padd_ba_rsp->peer_mac_addr[3], + padd_ba_rsp->peer_mac_addr[4], padd_ba_rsp->peer_mac_addr[5], + 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; + } else { + PRINTM(MERROR, "BA stream not created\n"); + } + } else { + mlan_11n_delete_bastream_tbl(priv, tid, padd_ba_rsp->peer_mac_addr, + TYPE_DELBA_SENT, MTRUE); + 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 */ + priv->aggr_prio_tbl[tid].ampdu_ap = BA_STREAM_NOT_ALLOWED; + + } else { + /* reset packet threshold */ + ra_list = + wlan_wmm_get_ralist_node(priv, tid, padd_ba_rsp->peer_mac_addr); + if (ra_list) { + ra_list->packet_count = 0; + ra_list->ba_packet_threshold = + wlan_get_random_ba_threshold(priv->adapter); + } + } + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @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); + 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(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND * resp, + IN 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(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND * cmd, + IN t_u16 cmd_action, IN 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(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND * resp, IN 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 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(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND * cmd, + IN t_u16 cmd_action, IN 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(pmadapter, txbfcfg->body.bf_periodicity.peer_mac, + txbf->body.bf_periodicity[0].peer_mac, 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(pmadapter, txbfcfg->body.bf_sound_args.peer_mac, + txbf->body.bf_sound[0].peer_mac, 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(pmadapter, txbfcfg->body.tx_bf_peer.peer_mac, + txbf->body.tx_bf_peer[0].peer_mac, 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(pmadapter, txbfcfg->body.bf_snr.peer_mac, + txbf->body.bf_snr[0].peer_mac, 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(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND * resp, IN 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(pmadapter, txbf->body.bf_sound[0].peer_mac, + txbfcfg->body.bf_sound_args.peer_mac, MLAN_MAC_ADDR_LENGTH); + txbf->body.bf_sound[0].status = txbfcfg->body.bf_sound_args.status; + break; + case SET_GET_BF_PERIODICITY: + memcpy(pmadapter, txbf->body.bf_periodicity->peer_mac, + txbfcfg->body.bf_periodicity.peer_mac, 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 < txbf->no_of_peers; i++) { + memcpy(pmadapter, txbf->body.tx_bf_peer[i].peer_mac, + (t_u8 *) tx_bf_peer->peer_mac, 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 < txbf->no_of_peers; i++) { + memcpy(pmadapter, txbf->body.bf_snr[i].peer_mac, + (t_u8 *) bf_snr->peer_mac, 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; +} + +#ifdef STA_SUPPORT +/** + * @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(IN mlan_private * pmpriv, + IN BSSDescriptor_t * pbss_desc, OUT t_u8 ** ppbuffer) +{ + pmlan_adapter pmadapter = pmpriv->adapter; + MrvlIETypes_HTCap_t *pht_cap; + MrvlIETypes_HTInfo_t *pht_info; + MrvlIEtypes_ChanListParamSet_t *pchan_list; + MrvlIETypes_2040BSSCo_t *p2040_bss_co; + MrvlIETypes_ExtCap_t *pext_cap; + t_u32 usr_dot_11n_dev_cap; + int ret_len = 0; + + ENTER(); + + /* Null Checks */ + if (ppbuffer == MNULL) { + LEAVE(); + return 0; + } + if (*ppbuffer == MNULL) { + LEAVE(); + return 0; + } + + if (pbss_desc->bss_band & BAND_A) + usr_dot_11n_dev_cap = pmadapter->usr_dot_11n_dev_cap_a; + else + usr_dot_11n_dev_cap = pmadapter->usr_dot_11n_dev_cap_bg; + + 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(pmadapter, (t_u8 *) pht_cap + sizeof(MrvlIEtypesHeader_t), + (t_u8 *) pbss_desc->pht_cap + sizeof(IEEEtypes_Header_t), + 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); + + 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); + } + + if (pbss_desc->pht_info) { + if (pmpriv->bss_mode == MLAN_BSS_MODE_IBSS) { + pht_info = (MrvlIETypes_HTInfo_t *) * ppbuffer; + memset(pmadapter, pht_info, 0, sizeof(MrvlIETypes_HTInfo_t)); + pht_info->header.type = wlan_cpu_to_le16(HT_OPERATION); + pht_info->header.len = sizeof(HTInfo_t); + + memcpy(pmadapter, (t_u8 *) pht_info + sizeof(MrvlIEtypesHeader_t), + (t_u8 *) pbss_desc->pht_info + sizeof(IEEEtypes_Header_t), + pht_info->header.len); + + if (!ISSUPP_CHANWIDTH40(usr_dot_11n_dev_cap)) { + RESET_CHANWIDTH40(pht_info->ht_info.field2); + } + + *ppbuffer += sizeof(MrvlIETypes_HTInfo_t); + ret_len += sizeof(MrvlIETypes_HTInfo_t); + pht_info->header.len = wlan_cpu_to_le16(pht_info->header.len); + } + + 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].radio_type = + wlan_band_to_radio_type((t_u8) pbss_desc->bss_band); + + if (ISSUPP_CHANWIDTH40(usr_dot_11n_dev_cap) && + ISALLOWED_CHANWIDTH40(pbss_desc->pht_info->ht_info.field2)) + SET_SECONDARYCHAN(pchan_list->chan_scan_param[0].radio_type, + GET_SECONDARYCHAN(pbss_desc->pht_info->ht_info. + field2)); + + 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(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); + + 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(pmadapter, (t_u8 *) pext_cap + sizeof(MrvlIEtypesHeader_t), + (t_u8 *) pbss_desc->pext_cap + sizeof(IEEEtypes_Header_t), + pext_cap->header.len); + + 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); + } + + 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(IN pmlan_adapter pmadapter, IN 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_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; + 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(); + + pmadapter->callbacks.moal_spin_lock(pmadapter->pmoal_handle, + priv->tx_ba_stream_tbl_ptr.plock); + + 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: + pmadapter->callbacks.moal_spin_unlock(pmadapter->pmoal_handle, + priv->tx_ba_stream_tbl_ptr.plock); + 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(); + + while ((del_tbl_ptr = (TxBAStreamTbl *) + util_peek_list(priv->adapter->pmoal_handle, + &priv->tx_ba_stream_tbl_ptr, + priv->adapter->callbacks.moal_spin_lock, + priv->adapter->callbacks.moal_spin_unlock))) { + wlan_11n_delete_txbastream_tbl_entry(priv, del_tbl_ptr); + } + + util_init_list((pmlan_linked_list) & priv->tx_ba_stream_tbl_ptr); + + 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 + * + * @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) +{ + TxBAStreamTbl *ptx_tbl; + pmlan_adapter pmadapter = priv->adapter; + + ENTER(); + + if (!(ptx_tbl = (TxBAStreamTbl *) util_peek_list(pmadapter->pmoal_handle, + &priv-> + tx_ba_stream_tbl_ptr, + pmadapter->callbacks. + moal_spin_lock, + pmadapter->callbacks. + moal_spin_unlock))) { + 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)) { + LEAVE(); + return ptx_tbl; + } + + ptx_tbl = ptx_tbl->pnext; + } + + LEAVE(); + return MNULL; +} + +/** + * @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 *newNode = MNULL; + pmlan_adapter pmadapter = priv->adapter; + + ENTER(); + + if (!wlan_11n_get_txbastream_tbl(priv, tid, ra)) { + PRINTM(MDAT_D, "get_txbastream_tbl TID %d\n", tid); + DBG_HEXDUMP(MDAT_D, "RA", ra, MLAN_MAC_ADDR_LENGTH); + + pmadapter->callbacks.moal_malloc(pmadapter->pmoal_handle, + sizeof(TxBAStreamTbl), MLAN_MEM_DEF, + (t_u8 **) & newNode); + util_init_list((pmlan_linked_list) newNode); + + newNode->tid = tid; + newNode->ba_status = ba_status; + memcpy(pmadapter, newNode->ra, ra, MLAN_MAC_ADDR_LENGTH); + + util_enqueue_list_tail(pmadapter->pmoal_handle, + &priv->tx_ba_stream_tbl_ptr, + (pmlan_linked_list) newNode, + pmadapter->callbacks.moal_spin_lock, + pmadapter->callbacks.moal_spin_unlock); + } + + 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\n", tid); + DBG_HEXDUMP(MCMD_D, "Send addba RA", peer_mac, MLAN_MAC_ADDR_LENGTH); + + 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(priv->adapter, &add_ba_req.peer_mac_addr, peer_mac, + 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 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_SUCCESS or MLAN_STATUS_FAILURE + */ +int +wlan_send_delba(mlan_private * priv, int tid, t_u8 * peer_mac, int initiator) +{ + HostCmd_DS_11N_DELBA delba; + mlan_status ret; + + ENTER(); + + 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(priv->adapter, &delba.peer_mac_addr, peer_mac, MLAN_MAC_ADDR_LENGTH); + + /* We don't wait for the response of this command */ + ret = wlan_prepare_cmd(priv, HostCmd_CMD_11N_DELBA, + HostCmd_ACT_GEN_SET, 0, MNULL, &delba); + + 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)); + + 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 *rxReorderTblPtr; + int count = 0; + ENTER(); + if (! + (rxReorderTblPtr = + (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))) { + LEAVE(); + return count; + } + while (rxReorderTblPtr != (RxReorderTbl *) & priv->rx_reorder_tbl_ptr) { + ptbl->tid = (t_u16) rxReorderTblPtr->tid; + memcpy(priv->adapter, ptbl->ta, rxReorderTblPtr->ta, + MLAN_MAC_ADDR_LENGTH); + ptbl->start_win = rxReorderTblPtr->start_win; + ptbl->win_size = rxReorderTblPtr->win_size; + ptbl->amsdu = rxReorderTblPtr->amsdu; + for (i = 0; i < rxReorderTblPtr->win_size; ++i) { + if (rxReorderTblPtr->rx_reorder_ptr[i]) + ptbl->buffer[i] = MTRUE; + else + ptbl->buffer[i] = MFALSE; + } + rxReorderTblPtr = rxReorderTblPtr->pnext; + ptbl++; + count++; + if (count >= MLAN_MAX_RX_BASTREAM_SUPPORTED) + break; + } + 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; + + ENTER(); + + if (!(ptxtbl = (TxBAStreamTbl *) util_peek_list(priv->adapter->pmoal_handle, + &priv->tx_ba_stream_tbl_ptr, + priv->adapter->callbacks. + moal_spin_lock, + priv->adapter->callbacks. + moal_spin_unlock))) { + LEAVE(); + return count; + } + + while (ptxtbl != (TxBAStreamTbl *) & priv->tx_ba_stream_tbl_ptr) { + ptbl->tid = (t_u16) ptxtbl->tid; + PRINTM(MINFO, "tid=%d\n", ptbl->tid); + memcpy(priv->adapter, ptbl->ra, ptxtbl->ra, MLAN_MAC_ADDR_LENGTH); + ptbl->amsdu = ptxtbl->amsdu; + ptxtbl = ptxtbl->pnext; + ptbl++; + count++; + if (count >= MLAN_MAX_TX_BASTREAM_SUPPORTED) + break; + } + + LEAVE(); + return count; +} + +/** + * @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(); + for (i = 0; i < MAX_NUM_TID; ++i) { + if ((ptx_tbl = wlan_11n_get_txbastream_tbl(priv, i, ra))) { + wlan_11n_delete_txbastream_tbl_entry(priv, ptx_tbl); + } + } + LEAVE(); + return; +} |