diff options
author | David S. Miller <davem@davemloft.net> | 2013-02-19 22:24:50 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2013-02-19 22:24:50 -0500 |
commit | c6b538079723efd21b5fd7e3091dc47dbd0436c4 (patch) | |
tree | 778a14480d95c1d118c188ab69ebc482f9d8f357 | |
parent | 4aa896c4baaab2b4f499c7434f85457f9f3d9fe4 (diff) | |
parent | 0b7164458fc184455239ea3676af1b362df1ce1d (diff) |
Merge branch 'for-davem' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next
John W. Linville says:
====================
One last batch of stragglers intended for 3.9...
For the iwlwifi pull, Johannes says:
"I hadn't expected to ask you to pull iwlwifi-next again, but I have a
number of fixes most of which I'd also send in after rc1, so here it is.
The first commit is a merge error between mac80211-next and
iwlwifi-next; in addition I have fixes for P2P scanning and MVM driver
MAC (virtual interface) management from Ilan, a CT-kill (critical
temperature) fix from Eytan, and myself fixed three different little but
annoying bugs in the MVM driver.
The only ones I might not send for -rc1 are Emmanuel's debug patch, but
OTOH it should help greatly if there are any issues, and my own time
event debugging patch that I used to find the race condition but we
decided to keep it for the future."
For the mac80211 pull, Johannes says:
"Like iwlwifi-next, this would almost be suitable for rc1.
I have a fix for station management on non-TDLS drivers, a CAB queue
crash fix for mesh, a fix for an annoying (but harmless) warning, a
tracing fix and a documentation fix. Other than that, only a few mesh
cleanups."
Along with that is a fix for memory corruption in rtlwifi, an
orinoco_usb fix to avoid allocating a DMA buffer on the stack, an a
hostap fix to return -ENOMEM instead of -1 after a memory allocation
failure. The remaining bits implement 802.11ac support for the mwifiex
driver -- I think that is still worth getting into 3.9.
Please let me know if there are problems!
====================
Signed-off-by: David S. Miller <davem@davemloft.net>
45 files changed, 1297 insertions, 444 deletions
diff --git a/Documentation/DocBook/80211.tmpl b/Documentation/DocBook/80211.tmpl index 42e7f030cb1..284ced7a228 100644 --- a/Documentation/DocBook/80211.tmpl +++ b/Documentation/DocBook/80211.tmpl @@ -107,8 +107,8 @@ !Finclude/net/cfg80211.h key_params !Finclude/net/cfg80211.h survey_info_flags !Finclude/net/cfg80211.h survey_info -!Finclude/net/cfg80211.h beacon_parameters -!Finclude/net/cfg80211.h plink_actions +!Finclude/net/cfg80211.h cfg80211_beacon_data +!Finclude/net/cfg80211.h cfg80211_ap_settings !Finclude/net/cfg80211.h station_parameters !Finclude/net/cfg80211.h station_info_flags !Finclude/net/cfg80211.h rate_info_flags diff --git a/drivers/net/wireless/hostap/hostap_ap.c b/drivers/net/wireless/hostap/hostap_ap.c index c6ea995750d..dd9a18f8dbc 100644 --- a/drivers/net/wireless/hostap/hostap_ap.c +++ b/drivers/net/wireless/hostap/hostap_ap.c @@ -376,7 +376,7 @@ int ap_control_add_mac(struct mac_restrictions *mac_restrictions, u8 *mac) entry = kmalloc(sizeof(struct mac_entry), GFP_KERNEL); if (entry == NULL) - return -1; + return -ENOMEM; memcpy(entry->addr, mac, ETH_ALEN); diff --git a/drivers/net/wireless/iwlwifi/dvm/commands.h b/drivers/net/wireless/iwlwifi/dvm/commands.h index 02c9ebb3b34..84e2c0fcfef 100644 --- a/drivers/net/wireless/iwlwifi/dvm/commands.h +++ b/drivers/net/wireless/iwlwifi/dvm/commands.h @@ -1403,6 +1403,7 @@ enum { #define AGG_TX_STATUS_MSK 0x00000fff /* bits 0:11 */ #define AGG_TX_TRY_MSK 0x0000f000 /* bits 12:15 */ +#define AGG_TX_TRY_POS 12 #define AGG_TX_STATE_LAST_SENT_MSK (AGG_TX_STATE_LAST_SENT_TTL_MSK | \ AGG_TX_STATE_LAST_SENT_TRY_CNT_MSK | \ diff --git a/drivers/net/wireless/iwlwifi/dvm/tt.c b/drivers/net/wireless/iwlwifi/dvm/tt.c index 67e2e1321b4..03f9bc01c0c 100644 --- a/drivers/net/wireless/iwlwifi/dvm/tt.c +++ b/drivers/net/wireless/iwlwifi/dvm/tt.c @@ -471,8 +471,8 @@ static void iwl_advance_tt_handler(struct iwl_priv *priv, s32 temp, bool force) set_bit(STATUS_CT_KILL, &priv->status); iwl_perform_ct_kill_task(priv, true); } else { - iwl_prepare_ct_kill_task(priv); tt->state = old_state; + iwl_prepare_ct_kill_task(priv); } } else if (old_state == IWL_TI_CT_KILL && tt->state != IWL_TI_CT_KILL) { diff --git a/drivers/net/wireless/iwlwifi/dvm/tx.c b/drivers/net/wireless/iwlwifi/dvm/tx.c index d1dccb36139..6aec2df3bb2 100644 --- a/drivers/net/wireless/iwlwifi/dvm/tx.c +++ b/drivers/net/wireless/iwlwifi/dvm/tx.c @@ -908,6 +908,12 @@ static void iwlagn_count_agg_tx_err_status(struct iwl_priv *priv, u16 status) } } +static inline u32 iwlagn_get_scd_ssn(struct iwlagn_tx_resp *tx_resp) +{ + return le32_to_cpup((__le32 *)&tx_resp->status + + tx_resp->frame_count) & MAX_SN; +} + static void iwl_rx_reply_tx_agg(struct iwl_priv *priv, struct iwlagn_tx_resp *tx_resp) { @@ -942,9 +948,15 @@ static void iwl_rx_reply_tx_agg(struct iwl_priv *priv, if (tx_resp->frame_count == 1) return; + IWL_DEBUG_TX_REPLY(priv, "TXQ %d initial_rate 0x%x ssn %d frm_cnt %d\n", + agg->txq_id, + le32_to_cpu(tx_resp->rate_n_flags), + iwlagn_get_scd_ssn(tx_resp), tx_resp->frame_count); + /* Construct bit-map of pending frames within Tx window */ for (i = 0; i < tx_resp->frame_count; i++) { u16 fstatus = le16_to_cpu(frame_status[i].status); + u8 retry_cnt = (fstatus & AGG_TX_TRY_MSK) >> AGG_TX_TRY_POS; if (status & AGG_TX_STATUS_MSK) iwlagn_count_agg_tx_err_status(priv, fstatus); @@ -952,6 +964,14 @@ static void iwl_rx_reply_tx_agg(struct iwl_priv *priv, if (status & (AGG_TX_STATE_FEW_BYTES_MSK | AGG_TX_STATE_ABORT_MSK)) continue; + + if (status & AGG_TX_STATUS_MSK || retry_cnt > 1) + IWL_DEBUG_TX_REPLY(priv, + "%d: status %s (0x%04x), try-count (0x%01x)\n", + i, + iwl_get_agg_tx_fail_reason(fstatus), + fstatus & AGG_TX_STATUS_MSK, + retry_cnt); } } @@ -982,12 +1002,6 @@ const char *iwl_get_agg_tx_fail_reason(u16 status) } #endif /* CONFIG_IWLWIFI_DEBUG */ -static inline u32 iwlagn_get_scd_ssn(struct iwlagn_tx_resp *tx_resp) -{ - return le32_to_cpup((__le32 *)&tx_resp->status + - tx_resp->frame_count) & MAX_SN; -} - static void iwlagn_count_tx_err_status(struct iwl_priv *priv, u16 status) { status &= TX_STATUS_MSK; @@ -1119,8 +1133,14 @@ int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, spin_lock_bh(&priv->sta_lock); - if (is_agg) + if (is_agg) { + WARN_ON_ONCE(sta_id >= IWLAGN_STATION_COUNT || + tid >= IWL_MAX_TID_COUNT); + if (txq_id != priv->tid_data[sta_id][tid].agg.txq_id) + IWL_ERR(priv, "txq_id mismatch: %d %d\n", txq_id, + priv->tid_data[sta_id][tid].agg.txq_id); iwl_rx_reply_tx_agg(priv, tx_resp); + } __skb_queue_head_init(&skbs); @@ -1224,16 +1244,17 @@ int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, */ if (is_offchannel_skb && freed != 1) IWL_ERR(priv, "OFFCHANNEL SKB freed %d\n", freed); - } - IWL_DEBUG_TX_REPLY(priv, "TXQ %d status %s (0x%08x)\n", txq_id, - iwl_get_tx_fail_reason(status), status); + IWL_DEBUG_TX_REPLY(priv, "TXQ %d status %s (0x%08x)\n", txq_id, + iwl_get_tx_fail_reason(status), status); - IWL_DEBUG_TX_REPLY(priv, - "\t\t\t\tinitial_rate 0x%x retries %d, idx=%d ssn=%d seq_ctl=0x%x\n", - le32_to_cpu(tx_resp->rate_n_flags), - tx_resp->failure_frame, SEQ_TO_INDEX(sequence), ssn, - le16_to_cpu(tx_resp->seq_ctl)); + IWL_DEBUG_TX_REPLY(priv, + "\t\t\t\tinitial_rate 0x%x retries %d, idx=%d ssn=%d seq_ctl=0x%x\n", + le32_to_cpu(tx_resp->rate_n_flags), + tx_resp->failure_frame, + SEQ_TO_INDEX(sequence), ssn, + le16_to_cpu(tx_resp->seq_ctl)); + } iwl_check_abort_status(priv, tx_resp->frame_count, status); spin_unlock_bh(&priv->sta_lock); diff --git a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c index 0854dc33888..341dbc0237e 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c @@ -245,6 +245,10 @@ static int iwl_mvm_mac_ctxt_allocate_resources(struct iwl_mvm *mvm, * that we should share it with another interface. */ + /* Currently, MAC ID 0 should be used only for the managed vif */ + if (vif->type != NL80211_IFTYPE_STATION || vif->p2p) + __clear_bit(0, data.available_mac_ids); + ieee80211_iterate_active_interfaces_atomic( mvm->hw, IEEE80211_IFACE_ITER_RESUME_ALL, iwl_mvm_mac_iface_iterator, &data); @@ -286,6 +290,9 @@ static int iwl_mvm_mac_ctxt_allocate_resources(struct iwl_mvm *mvm, mvmvif->color = 0; + INIT_LIST_HEAD(&mvmvif->time_event_data.list); + mvmvif->time_event_data.id = TE_MAX; + /* No need to allocate data queues to P2P Device MAC.*/ if (vif->type == NL80211_IFTYPE_P2P_DEVICE) { for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) @@ -328,9 +335,6 @@ static int iwl_mvm_mac_ctxt_allocate_resources(struct iwl_mvm *mvm, mvmvif->bcast_sta.sta_id = IWL_MVM_STATION_COUNT; mvmvif->ap_sta_id = IWL_MVM_STATION_COUNT; - INIT_LIST_HEAD(&mvmvif->time_event_data.list); - mvmvif->time_event_data.id = TE_MAX; - return 0; exit_fail: @@ -585,10 +589,43 @@ static void iwl_mvm_mac_ctxt_cmd_fill_sta(struct iwl_mvm *mvm, struct iwl_mac_data_sta *ctxt_sta) { /* We need the dtim_period to set the MAC as associated */ - if (vif->bss_conf.assoc && vif->bss_conf.dtim_period) + if (vif->bss_conf.assoc && vif->bss_conf.dtim_period) { + u32 dtim_offs; + + /* + * The DTIM count counts down, so when it is N that means N + * more beacon intervals happen until the DTIM TBTT. Therefore + * add this to the current time. If that ends up being in the + * future, the firmware will handle it. + * + * Also note that the system_timestamp (which we get here as + * "sync_device_ts") and TSF timestamp aren't at exactly the + * same offset in the frame -- the TSF is at the first symbol + * of the TSF, the system timestamp is at signal acquisition + * time. This means there's an offset between them of at most + * a few hundred microseconds (24 * 8 bits + PLCP time gives + * 384us in the longest case), this is currently not relevant + * as the firmware wakes up around 2ms before the TBTT. + */ + dtim_offs = vif->bss_conf.sync_dtim_count * + vif->bss_conf.beacon_int; + /* convert TU to usecs */ + dtim_offs *= 1024; + + ctxt_sta->dtim_tsf = + cpu_to_le64(vif->bss_conf.sync_tsf + dtim_offs); + ctxt_sta->dtim_time = + cpu_to_le32(vif->bss_conf.sync_device_ts + dtim_offs); + + IWL_DEBUG_INFO(mvm, "DTIM TBTT is 0x%llx/0x%x, offset %d\n", + le64_to_cpu(ctxt_sta->dtim_tsf), + le32_to_cpu(ctxt_sta->dtim_time), + dtim_offs); + ctxt_sta->is_assoc = cpu_to_le32(1); - else + } else { ctxt_sta->is_assoc = cpu_to_le32(0); + } ctxt_sta->bi = cpu_to_le32(vif->bss_conf.beacon_int); ctxt_sta->bi_reciprocal = diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index e27eb972411..e8264e11b12 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -113,10 +113,10 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) IEEE80211_HW_REPORTS_TX_ACK_STATUS | IEEE80211_HW_QUEUE_CONTROL | IEEE80211_HW_WANT_MONITOR_VIF | - IEEE80211_HW_NEED_DTIM_BEFORE_ASSOC | IEEE80211_HW_SUPPORTS_PS | IEEE80211_HW_SUPPORTS_DYNAMIC_PS | - IEEE80211_HW_AMPDU_AGGREGATION; + IEEE80211_HW_AMPDU_AGGREGATION | + IEEE80211_HW_TIMING_BEACON_ONLY; hw->queues = IWL_FIRST_AMPDU_QUEUE; hw->offchannel_tx_hw_queue = IWL_OFFCHANNEL_QUEUE; @@ -857,7 +857,6 @@ iwl_mvm_mac_allow_buffered_frames(struct ieee80211_hw *hw, bool more_data) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - struct iwl_mvm_sta *mvmsta = (void *)sta->drv_priv; /* TODO: how do we tell the fw to send frames for a specific TID */ @@ -865,8 +864,7 @@ iwl_mvm_mac_allow_buffered_frames(struct ieee80211_hw *hw, * The fw will send EOSP notification when the last frame will be * transmitted. */ - iwl_mvm_sta_modify_sleep_tx_count(mvm, mvmsta->sta_id, reason, - num_frames); + iwl_mvm_sta_modify_sleep_tx_count(mvm, sta, reason, num_frames); } static void iwl_mvm_mac_sta_notify(struct ieee80211_hw *hw, @@ -890,7 +888,7 @@ static void iwl_mvm_mac_sta_notify(struct ieee80211_hw *hw, case STA_NOTIFY_AWAKE: if (WARN_ON(mvmsta->sta_id == IWL_INVALID_STATION)) break; - iwl_mvm_sta_modify_ps_wake(mvm, mvmsta->sta_id); + iwl_mvm_sta_modify_ps_wake(mvm, sta); break; default: break; diff --git a/drivers/net/wireless/iwlwifi/mvm/rx.c b/drivers/net/wireless/iwlwifi/mvm/rx.c index 3f3ce91ad5c..3f40ab05bbd 100644 --- a/drivers/net/wireless/iwlwifi/mvm/rx.c +++ b/drivers/net/wireless/iwlwifi/mvm/rx.c @@ -267,6 +267,7 @@ int iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, /* rx_status carries information about the packet to mac80211 */ rx_status.mactime = le64_to_cpu(phy_info->timestamp); + rx_status.device_timestamp = le32_to_cpu(phy_info->system_timestamp); rx_status.band = (phy_info->phy_flags & cpu_to_le16(RX_RES_PHY_FLAGS_BAND_24)) ? IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ; diff --git a/drivers/net/wireless/iwlwifi/mvm/scan.c b/drivers/net/wireless/iwlwifi/mvm/scan.c index 406c53ad0a4..9b21b92aa8d 100644 --- a/drivers/net/wireless/iwlwifi/mvm/scan.c +++ b/drivers/net/wireless/iwlwifi/mvm/scan.c @@ -292,7 +292,12 @@ int iwl_mvm_scan_request(struct iwl_mvm *mvm, cmd->rxon_flags = iwl_mvm_scan_rxon_flags(req); cmd->filter_flags = cpu_to_le32(MAC_FILTER_ACCEPT_GRP | MAC_FILTER_IN_BEACON); - cmd->type = SCAN_TYPE_FORCED; + + if (vif->type == NL80211_IFTYPE_P2P_DEVICE) + cmd->type = cpu_to_le32(SCAN_TYPE_DISCOVERY_FORCED); + else + cmd->type = cpu_to_le32(SCAN_TYPE_FORCED); + cmd->repeats = cpu_to_le32(1); /* diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.c b/drivers/net/wireless/iwlwifi/mvm/sta.c index a1eb692d7fa..861a7f9f8e7 100644 --- a/drivers/net/wireless/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/iwlwifi/mvm/sta.c @@ -1188,13 +1188,16 @@ void iwl_mvm_update_tkip_key(struct iwl_mvm *mvm, rcu_read_unlock(); } -void iwl_mvm_sta_modify_ps_wake(struct iwl_mvm *mvm, int sta_id) +void iwl_mvm_sta_modify_ps_wake(struct iwl_mvm *mvm, + struct ieee80211_sta *sta) { + struct iwl_mvm_sta *mvmsta = (void *)sta->drv_priv; struct iwl_mvm_add_sta_cmd cmd = { .add_modify = STA_MODE_MODIFY, - .sta_id = sta_id, + .sta_id = mvmsta->sta_id, .modify_mask = STA_MODIFY_SLEEPING_STA_TX_COUNT, .sleep_state_flags = cpu_to_le16(STA_SLEEP_STATE_AWAKE), + .mac_id_n_color = cpu_to_le32(mvmsta->mac_id_n_color), }; int ret; @@ -1208,18 +1211,21 @@ void iwl_mvm_sta_modify_ps_wake(struct iwl_mvm *mvm, int sta_id) IWL_ERR(mvm, "Failed to send ADD_STA command (%d)\n", ret); } -void iwl_mvm_sta_modify_sleep_tx_count(struct iwl_mvm *mvm, int sta_id, +void iwl_mvm_sta_modify_sleep_tx_count(struct iwl_mvm *mvm, + struct ieee80211_sta *sta, enum ieee80211_frame_release_type reason, u16 cnt) { u16 sleep_state_flags = (reason == IEEE80211_FRAME_RELEASE_UAPSD) ? STA_SLEEP_STATE_UAPSD : STA_SLEEP_STATE_PS_POLL; + struct iwl_mvm_sta *mvmsta = (void *)sta->drv_priv; struct iwl_mvm_add_sta_cmd cmd = { .add_modify = STA_MODE_MODIFY, - .sta_id = sta_id, + .sta_id = mvmsta->sta_id, .modify_mask = STA_MODIFY_SLEEPING_STA_TX_COUNT, .sleep_tx_count = cpu_to_le16(cnt), + .mac_id_n_color = cpu_to_le32(mvmsta->mac_id_n_color), /* * Same modify mask for sleep_tx_count and sleep_state_flags so * we must set the sleep_state_flags too. diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.h b/drivers/net/wireless/iwlwifi/mvm/sta.h index bdd7c5ed822..896f88ac814 100644 --- a/drivers/net/wireless/iwlwifi/mvm/sta.h +++ b/drivers/net/wireless/iwlwifi/mvm/sta.h @@ -362,8 +362,10 @@ int iwl_mvm_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct iwl_mvm_int_sta *bsta); int iwl_mvm_rm_bcast_sta(struct iwl_mvm *mvm, struct iwl_mvm_int_sta *bsta); void iwl_mvm_sta_drained_wk(struct work_struct *wk); -void iwl_mvm_sta_modify_ps_wake(struct iwl_mvm *mvm, int sta_id); -void iwl_mvm_sta_modify_sleep_tx_count(struct iwl_mvm *mvm, int sta_id, +void iwl_mvm_sta_modify_ps_wake(struct iwl_mvm *mvm, + struct ieee80211_sta *sta); +void iwl_mvm_sta_modify_sleep_tx_count(struct iwl_mvm *mvm, + struct ieee80211_sta *sta, enum ieee80211_frame_release_type reason, u16 cnt); int iwl_mvm_drain_sta(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta, diff --git a/drivers/net/wireless/iwlwifi/mvm/time-event.c b/drivers/net/wireless/iwlwifi/mvm/time-event.c index c09b71f2375..e437e02c714 100644 --- a/drivers/net/wireless/iwlwifi/mvm/time-event.c +++ b/drivers/net/wireless/iwlwifi/mvm/time-event.c @@ -248,6 +248,11 @@ static bool iwl_mvm_time_event_response(struct iwl_notif_wait_data *notif_wait, } resp = (void *)pkt->data; + + /* we should never get a response to another TIME_EVENT_CMD here */ + if (WARN_ON_ONCE(le32_to_cpu(resp->id) != te_data->id)) + return false; + te_data->uid = le32_to_cpu(resp->unique_id); IWL_DEBUG_TE(mvm, "TIME_EVENT_CMD response - UID = 0x%x\n", te_data->uid); @@ -265,6 +270,9 @@ static int iwl_mvm_time_event_send_add(struct iwl_mvm *mvm, lockdep_assert_held(&mvm->mutex); + IWL_DEBUG_TE(mvm, "Add new TE, duration %d TU\n", + le32_to_cpu(te_cmd->duration)); + spin_lock_bh(&mvm->time_event_lock); if (WARN_ON(te_data->id != TE_MAX)) { spin_unlock_bh(&mvm->time_event_lock); @@ -413,7 +421,7 @@ void iwl_mvm_remove_time_event(struct iwl_mvm *mvm, cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color)); IWL_DEBUG_TE(mvm, "Removing TE 0x%x\n", le32_to_cpu(time_cmd.id)); - ret = iwl_mvm_send_cmd_pdu(mvm, TIME_EVENT_CMD, CMD_ASYNC, + ret = iwl_mvm_send_cmd_pdu(mvm, TIME_EVENT_CMD, CMD_SYNC, sizeof(time_cmd), &time_cmd); if (WARN_ON(ret)) return; diff --git a/drivers/net/wireless/mwifiex/11ac.c b/drivers/net/wireless/mwifiex/11ac.c new file mode 100644 index 00000000000..cf43b3c2925 --- /dev/null +++ b/drivers/net/wireless/mwifiex/11ac.c @@ -0,0 +1,261 @@ +/* + * Marvell Wireless LAN device driver: 802.11ac + * + * Copyright (C) 2013, 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. + */ + +#include "decl.h" +#include "ioctl.h" +#include "fw.h" +#include "main.h" +#include "11ac.h" + +/* This function converts the 2-bit MCS map to the highest long GI + * VHT data rate. + */ +static u16 +mwifiex_convert_mcsmap_to_maxrate(struct mwifiex_private *priv, + u8 bands, u16 mcs_map) +{ + u8 i, nss, max_mcs; + u16 max_rate = 0; + u32 usr_vht_cap_info = 0; + struct mwifiex_adapter *adapter = priv->adapter; + /* tables of the MCS map to the highest data rate (in Mbps) + * supported for long GI + */ + u16 max_rate_lgi_80MHZ[8][3] = { + {0x124, 0x15F, 0x186}, /* NSS = 1 */ + {0x249, 0x2BE, 0x30C}, /* NSS = 2 */ + {0x36D, 0x41D, 0x492}, /* NSS = 3 */ + {0x492, 0x57C, 0x618}, /* NSS = 4 */ + {0x5B6, 0x6DB, 0x79E}, /* NSS = 5 */ + {0x6DB, 0x83A, 0x0}, /* NSS = 6 */ + {0x7FF, 0x999, 0xAAA}, /* NSS = 7 */ + {0x924, 0xAF8, 0xC30} /* NSS = 8 */ + }; + u16 max_rate_lgi_160MHZ[8][3] = { + {0x249, 0x2BE, 0x30C}, /* NSS = 1 */ + {0x492, 0x57C, 0x618}, /* NSS = 2 */ + {0x6DB, 0x83A, 0x0}, /* NSS = 3 */ + {0x924, 0xAF8, 0xC30}, /* NSS = 4 */ + {0xB6D, 0xDB6, 0xF3C}, /* NSS = 5 */ + {0xDB6, 0x1074, 0x1248}, /* NSS = 6 */ + {0xFFF, 0x1332, 0x1554}, /* NSS = 7 */ + {0x1248, 0x15F0, 0x1860} /* NSS = 8 */ + }; + + if (bands & BAND_AAC) + usr_vht_cap_info = adapter->usr_dot_11ac_dev_cap_a; + else + usr_vht_cap_info = adapter->usr_dot_11ac_dev_cap_bg; + + /* find the max NSS supported */ + nss = 0; + for (i = 0; i < 8; i++) { + max_mcs = (mcs_map >> (2 * i)) & 0x3; + if (max_mcs < 3) + nss = i; + } + max_mcs = (mcs_map >> (2 * nss)) & 0x3; + + /* if max_mcs is 3, nss must be 0 (SS = 1). Thus, max mcs is MCS 9 */ + if (max_mcs >= 3) + max_mcs = 2; + + if (GET_VHTCAP_CHWDSET(usr_vht_cap_info)) { + /* support 160 MHz */ + max_rate = max_rate_lgi_160MHZ[nss][max_mcs]; + if (!max_rate) + /* MCS9 is not supported in NSS6 */ + max_rate = max_rate_lgi_160MHZ[nss][max_mcs - 1]; + } else { + max_rate = max_rate_lgi_80MHZ[nss][max_mcs]; + if (!max_rate) + /* MCS9 is not supported in NSS3 */ + max_rate = max_rate_lgi_80MHZ[nss][max_mcs - 1]; + } + + return max_rate; +} + +static void +mwifiex_fill_vht_cap_info(struct mwifiex_private *priv, + struct mwifiex_ie_types_vhtcap *vht_cap, u8 bands) +{ + struct mwifiex_adapter *adapter = priv->adapter; + + if (bands & BAND_A) + vht_cap->vht_cap.vht_cap_info = + cpu_to_le32(adapter->usr_dot_11ac_dev_cap_a); + else + vht_cap->vht_cap.vht_cap_info = + cpu_to_le32(adapter->usr_dot_11ac_dev_cap_bg); +} + +static void +mwifiex_fill_vht_cap_tlv(struct mwifiex_private *priv, + struct mwifiex_ie_types_vhtcap *vht_cap, u8 bands) +{ + struct mwifiex_adapter *adapter = priv->adapter; + u16 mcs_map_user, mcs_map_resp, mcs_map_result; + u16 mcs_user, mcs_resp, nss, tmp; + + /* Fill VHT cap info */ + mwifiex_fill_vht_cap_info(priv, vht_cap, bands); + + /* rx MCS Set: find the minimum of the user rx mcs and ap rx mcs */ + mcs_map_user = GET_DEVRXMCSMAP(adapter->usr_dot_11ac_mcs_support); + mcs_map_resp = le16_to_cpu(vht_cap->vht_cap.supp_mcs.rx_mcs_map); + mcs_map_result = 0; + + for (nss = 1; nss <= 8; nss++) { + mcs_user = GET_VHTNSSMCS(mcs_map_user, nss); + mcs_resp = GET_VHTNSSMCS(mcs_map_resp, nss); + + if ((mcs_user == NO_NSS_SUPPORT) || + (mcs_resp == NO_NSS_SUPPORT)) + SET_VHTNSSMCS(mcs_map_result, nss, NO_NSS_SUPPORT); + else + SET_VHTNSSMCS(mcs_map_result, nss, + min(mcs_user, mcs_resp)); + } + + vht_cap->vht_cap.supp_mcs.rx_mcs_map = cpu_to_le16(mcs_map_result); + + tmp = mwifiex_convert_mcsmap_to_maxrate(priv, bands, mcs_map_result); + vht_cap->vht_cap.supp_mcs.rx_highest = cpu_to_le16(tmp); + + /* tx MCS Set: find the minimum of the user tx mcs and ap tx mcs */ + mcs_map_user = GET_DEVTXMCSMAP(adapter->usr_dot_11ac_mcs_support); + mcs_map_resp = le16_to_cpu(vht_cap->vht_cap.supp_mcs.tx_mcs_map); + mcs_map_result = 0; + + for (nss = 1; nss <= 8; nss++) { + mcs_user = GET_VHTNSSMCS(mcs_map_user, nss); + mcs_resp = GET_VHTNSSMCS(mcs_map_resp, nss); + if ((mcs_user == NO_NSS_SUPPORT) || + (mcs_resp == NO_NSS_SUPPORT)) + SET_VHTNSSMCS(mcs_map_result, nss, NO_NSS_SUPPORT); + else + SET_VHTNSSMCS(mcs_map_result, nss, + min(mcs_user, mcs_resp)); + } + + vht_cap->vht_cap.supp_mcs.tx_mcs_map = cpu_to_le16(mcs_map_result); + + tmp = mwifiex_convert_mcsmap_to_maxrate(priv, bands, mcs_map_result); + vht_cap->vht_cap.supp_mcs.tx_highest = cpu_to_le16(tmp); + + return; +} + +int mwifiex_cmd_append_11ac_tlv(struct mwifiex_private *priv, + struct mwifiex_bssdescriptor *bss_desc, + u8 **buffer) +{ + struct mwifiex_ie_types_vhtcap *vht_cap; + struct mwifiex_ie_types_oper_mode_ntf *oper_ntf; + struct ieee_types_oper_mode_ntf *ieee_oper_ntf; + struct mwifiex_ie_types_vht_oper *vht_op; + struct mwifiex_adapter *adapter = priv->adapter; + u8 supp_chwd_set; + u32 usr_vht_cap_info; + int ret_len = 0; + + if (bss_desc->bss_band & BAND_A) + usr_vht_cap_info = adapter->usr_dot_11ac_dev_cap_a; |