You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
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>
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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 | \
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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,17 +1244,18 @@ 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,
|
||||
"\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, "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_check_abort_status(priv, tx_resp->frame_count, status);
|
||||
spin_unlock_bh(&priv->sta_lock);
|
||||
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
/*
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
else
|
||||
usr_vht_cap_info = adapter->usr_dot_11ac_dev_cap_bg;
|
||||
|
||||
/* VHT Capabilities IE */
|
||||
if (bss_desc->bcn_vht_cap) {
|
||||
vht_cap = (struct mwifiex_ie_types_vhtcap *)*buffer;
|
||||
memset(vht_cap, 0, sizeof(*vht_cap));
|
||||
vht_cap->header.type = cpu_to_le16(WLAN_EID_VHT_CAPABILITY);
|
||||
vht_cap->header.len =
|
||||
cpu_to_le16(sizeof(struct ieee80211_vht_cap));
|
||||
memcpy((u8 *)vht_cap + sizeof(struct mwifiex_ie_types_header),
|
||||
(u8 *)bss_desc->bcn_vht_cap +
|
||||
sizeof(struct ieee_types_header),
|
||||
le16_to_cpu(vht_cap->header.len));
|
||||
|
||||
mwifiex_fill_vht_cap_tlv(priv, vht_cap, bss_desc->bss_band);
|
||||
*buffer += sizeof(*vht_cap);
|
||||
ret_len += sizeof(*vht_cap);
|
||||
}
|
||||
|
||||
/* VHT Operation IE */
|
||||
if (bss_desc->bcn_vht_oper) {
|
||||
if (priv->bss_mode == HostCmd_BSS_MODE_IBSS) {
|
||||
vht_op = (struct mwifiex_ie_types_vht_oper *)*buffer;
|
||||
memset(vht_op, 0, sizeof(*vht_op));
|
||||
vht_op->header.type =
|
||||
cpu_to_le16(WLAN_EID_VHT_OPERATION);
|
||||
vht_op->header.len = cpu_to_le16(sizeof(*vht_op) -
|
||||
sizeof(struct mwifiex_ie_types_header));
|
||||
memcpy((u8 *)vht_op +
|
||||
sizeof(struct mwifiex_ie_types_header),
|
||||
(u8 *)bss_desc->bcn_vht_oper +
|
||||
sizeof(struct ieee_types_header),
|
||||
le16_to_cpu(vht_op->header.len));
|
||||
|
||||
/* negotiate the channel width and central freq
|
||||
* and keep the central freq as the peer suggests
|
||||
*/
|
||||
supp_chwd_set = GET_VHTCAP_CHWDSET(usr_vht_cap_info);
|
||||
|
||||
switch (supp_chwd_set) {
|
||||
case 0:
|
||||
vht_op->chan_width =
|
||||
min_t(u8, IEEE80211_VHT_CHANWIDTH_80MHZ,
|
||||
bss_desc->bcn_vht_oper->chan_width);
|
||||
break;
|
||||
case 1:
|
||||
vht_op->chan_width =
|
||||
min_t(u8, IEEE80211_VHT_CHANWIDTH_160MHZ,
|
||||
bss_desc->bcn_vht_oper->chan_width);
|
||||
break;
|
||||
case 2:
|
||||
vht_op->chan_width =
|
||||
min_t(u8, IEEE80211_VHT_CHANWIDTH_80P80MHZ,
|
||||
bss_desc->bcn_vht_oper->chan_width);
|
||||
break;
|
||||
default:
|
||||
vht_op->chan_width =
|
||||
IEEE80211_VHT_CHANWIDTH_USE_HT;
|
||||
break;
|
||||
}
|
||||
|
||||
*buffer += sizeof(*vht_op);
|
||||
ret_len += sizeof(*vht_op);
|
||||
}
|
||||
}
|
||||
|
||||
/* Operating Mode Notification IE */
|
||||
if (bss_desc->oper_mode) {
|
||||
ieee_oper_ntf = bss_desc->oper_mode;
|
||||
oper_ntf = (void *)*buffer;
|
||||
memset(oper_ntf, 0, sizeof(*oper_ntf));
|
||||
oper_ntf->header.type = cpu_to_le16(WLAN_EID_OPMODE_NOTIF);
|
||||
oper_ntf->header.len = cpu_to_le16(sizeof(u8));
|
||||
oper_ntf->oper_mode = ieee_oper_ntf->oper_mode;
|
||||
*buffer += sizeof(*oper_ntf);
|
||||
ret_len += sizeof(*oper_ntf);
|
||||
}
|
||||
|
||||
return ret_len;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _MWIFIEX_11AC_H_
|
||||
#define _MWIFIEX_11AC_H_
|
||||
|
||||
int mwifiex_cmd_append_11ac_tlv(struct mwifiex_private *priv,
|
||||
struct mwifiex_bssdescriptor *bss_desc,
|
||||
u8 **buffer);
|
||||
#endif /* _MWIFIEX_11AC_H_ */
|
||||
@@ -250,7 +250,8 @@ int mwifiex_cmd_amsdu_aggr_ctrl(struct host_cmd_ds_command *cmd,
|
||||
* - Setting HT Tx capability and HT Tx information fields
|
||||
* - Ensuring correct endian-ness
|
||||
*/
|
||||
int mwifiex_cmd_11n_cfg(struct host_cmd_ds_command *cmd, u16 cmd_action,
|
||||
int mwifiex_cmd_11n_cfg(struct mwifiex_private *priv,
|
||||
struct host_cmd_ds_command *cmd, u16 cmd_action,
|
||||
struct mwifiex_ds_11n_tx_cfg *txcfg)
|
||||
{
|
||||
struct host_cmd_ds_11n_cfg *htcfg = &cmd->params.htcfg;
|
||||
@@ -260,6 +261,10 @@ int mwifiex_cmd_11n_cfg(struct host_cmd_ds_command *cmd, u16 cmd_action,
|
||||
htcfg->action = cpu_to_le16(cmd_action);
|
||||
htcfg->ht_tx_cap = cpu_to_le16(txcfg->tx_htcap);
|
||||
htcfg->ht_tx_info = cpu_to_le16(txcfg->tx_htinfo);
|
||||
|
||||
if (priv->adapter->is_hw_11ac_capable)
|
||||
htcfg->misc_config = cpu_to_le16(txcfg->misc_config);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -28,9 +28,9 @@ int mwifiex_ret_11n_delba(struct mwifiex_private *priv,
|
||||
struct host_cmd_ds_command *resp);
|
||||
int mwifiex_ret_11n_addba_req(struct mwifiex_private *priv,
|
||||
struct host_cmd_ds_command *resp);
|
||||
int mwifiex_cmd_11n_cfg(struct host_cmd_ds_command *cmd, u16 cmd_action,
|
||||
int mwifiex_cmd_11n_cfg(struct mwifiex_private *priv,
|
||||
struct host_cmd_ds_command *cmd, u16 cmd_action,
|
||||
struct mwifiex_ds_11n_tx_cfg *txcfg);
|
||||
|
||||
int mwifiex_cmd_append_11n_tlv(struct mwifiex_private *priv,
|
||||
struct mwifiex_bssdescriptor *bss_desc,
|
||||
u8 **buffer);
|
||||
|
||||
@@ -23,6 +23,7 @@ mwifiex-y += util.o
|
||||
mwifiex-y += txrx.o
|
||||
mwifiex-y += wmm.o
|
||||
mwifiex-y += 11n.o
|
||||
mwifiex-y += 11ac.o
|
||||
mwifiex-y += 11n_aggr.o
|
||||
mwifiex-y += 11n_rxreorder.o
|
||||
mwifiex-y += scan.o
|
||||
|
||||
@@ -834,6 +834,66 @@ mwifiex_cfg80211_change_virtual_intf(struct wiphy *wiphy,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
mwifiex_parse_htinfo(struct mwifiex_private *priv, u8 tx_htinfo,
|
||||
struct rate_info *rate)
|
||||
{
|
||||
struct mwifiex_adapter *adapter = priv->adapter;
|
||||
|
||||
if (adapter->is_hw_11ac_capable) {
|
||||
/* bit[1-0]: 00=LG 01=HT 10=VHT */
|
||||
if (tx_htinfo & BIT(0)) {
|
||||
/* HT */
|
||||
rate->mcs = priv->tx_rate;
|
||||
rate->flags |= RATE_INFO_FLAGS_MCS;
|
||||
}
|
||||
if (tx_htinfo & BIT(1)) {
|
||||
/* VHT */
|
||||
rate->mcs = priv->tx_rate & 0x0F;
|
||||
rate->flags |= RATE_INFO_FLAGS_VHT_MCS;
|
||||
}
|
||||
|
||||
if (tx_htinfo & (BIT(1) | BIT(0))) {
|
||||
/* HT or VHT */
|
||||
switch (tx_htinfo & (BIT(3) | BIT(2))) {
|
||||
case 0:
|
||||
/* This will be 20MHz */
|
||||
break;
|
||||
case (BIT(2)):
|
||||
rate->flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
|
||||
break;
|
||||
case (BIT(3)):
|
||||
rate->flags |= RATE_INFO_FLAGS_80_MHZ_WIDTH;
|
||||
break;
|
||||
case (BIT(3) | BIT(2)):
|
||||
rate->flags |= RATE_INFO_FLAGS_160_MHZ_WIDTH;
|
||||
break;
|
||||
}
|
||||
|
||||
if (tx_htinfo & BIT(4))
|
||||
rate->flags |= RATE_INFO_FLAGS_SHORT_GI;
|
||||
|
||||
if ((priv->tx_rate >> 4) == 1)
|
||||
rate->nss = 2;
|
||||
else
|
||||
rate->nss = 1;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* Bit 0 in tx_htinfo indicates that current Tx rate
|
||||
* is 11n rate. Valid MCS index values for us are 0 to 15.
|
||||
*/
|
||||
if ((tx_htinfo & BIT(0)) && (priv->tx_rate < 16)) {
|
||||
rate->mcs = priv->tx_rate;
|
||||
rate->flags |= RATE_INFO_FLAGS_MCS;
|
||||
if (tx_htinfo & BIT(1))
|
||||
rate->flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
|
||||
if (tx_htinfo & BIT(2))
|
||||
rate->flags |= RATE_INFO_FLAGS_SHORT_GI;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This function dumps the station information on a buffer.
|
||||
*
|
||||
@@ -873,20 +933,7 @@ mwifiex_dump_station_info(struct mwifiex_private *priv,
|
||||
HostCmd_ACT_GEN_GET, DTIM_PERIOD_I,
|
||||
&priv->dtim_period);
|
||||
|
||||
/*
|
||||
* Bit 0 in tx_htinfo indicates that current Tx rate is 11n rate. Valid
|
||||
* MCS index values for us are 0 to 15.
|
||||
*/
|
||||
if ((priv->tx_htinfo & BIT(0)) && (priv->tx_rate < 16)) {
|
||||
sinfo->txrate.mcs = priv->tx_rate;
|
||||
sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS;
|
||||
/* 40MHz rate */
|
||||
if (priv->tx_htinfo & BIT(1))
|
||||
sinfo->txrate.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
|
||||
/* SGI enabled */
|
||||
if (priv->tx_htinfo & BIT(2))
|
||||
sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
|
||||
}
|
||||
mwifiex_parse_htinfo(priv, priv->tx_htinfo, &sinfo->txrate);
|
||||
|
||||
sinfo->signal_avg = priv->bcn_rssi_avg;
|
||||
sinfo->rx_bytes = priv->stats.rx_bytes;
|
||||
@@ -1295,20 +1342,22 @@ static int mwifiex_cfg80211_start_ap(struct wiphy *wiphy,
|
||||
/* Set appropriate bands */
|
||||
if (params->chandef.chan->band == IEEE80211_BAND_2GHZ) {
|
||||
bss_cfg->band_cfg = BAND_CONFIG_BG;
|
||||
config_bands = BAND_B | BAND_G;
|
||||
|
||||
if (cfg80211_get_chandef_type(¶ms->chandef) ==
|
||||
NL80211_CHAN_NO_HT)
|
||||
config_bands = BAND_B | BAND_G;
|
||||
else
|
||||
config_bands = BAND_B | BAND_G | BAND_GN;
|
||||
if (params->chandef.width > NL80211_CHAN_WIDTH_20_NOHT)
|
||||
config_bands |= BAND_GN;
|
||||
|
||||
if (params->chandef.width > NL80211_CHAN_WIDTH_40)
|
||||
config_bands |= BAND_GAC;
|
||||
} else {
|
||||
bss_cfg->band_cfg = BAND_CONFIG_A;
|
||||
config_bands = BAND_A;
|
||||
|
||||
if (cfg80211_get_chandef_type(¶ms->chandef) ==
|
||||
NL80211_CHAN_NO_HT)
|
||||
config_bands = BAND_A;
|
||||
else
|
||||
config_bands = BAND_AN | BAND_A;
|
||||
if (params->chandef.width > NL80211_CHAN_WIDTH_20_NOHT)
|
||||
config_bands |= BAND_AN;
|
||||
|
||||
if (params->chandef.width > NL80211_CHAN_WIDTH_40)
|
||||
config_bands |= BAND_AAC;
|
||||
}
|
||||
|
||||
if (!((config_bands | priv->adapter->fw_bands) &
|
||||
@@ -1879,6 +1928,79 @@ mwifiex_cfg80211_scan(struct wiphy *wiphy,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mwifiex_setup_vht_caps(struct ieee80211_sta_vht_cap *vht_info,
|
||||
struct mwifiex_private *priv)
|
||||
{
|
||||
struct mwifiex_adapter *adapter = priv->adapter;
|
||||
u32 vht_cap = 0, cap = adapter->hw_dot_11ac_dev_cap;
|
||||
|
||||
vht_info->vht_supported = true;
|
||||
|
||||
switch (GET_VHTCAP_MAXMPDULEN(cap)) {
|
||||
case 0x00:
|
||||
vht_cap |= IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895;
|
||||
break;
|
||||
case 0x01:
|
||||
vht_cap |= IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991;
|
||||
break;
|
||||
case 0x10:
|
||||
vht_cap |= IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454;
|
||||
break;
|
||||
default:
|
||||
dev_err(adapter->dev, "unsupported MAX MPDU len\n");
|
||||
break;
|
||||
}
|
||||
|
||||
if (ISSUPP_11ACVHTHTCVHT(cap))
|
||||
vht_cap |= IEEE80211_VHT_CAP_HTC_VHT;
|
||||
|
||||
if (ISSUPP_11ACVHTTXOPPS(cap))
|
||||
vht_cap |= IEEE80211_VHT_CAP_VHT_TXOP_PS;
|
||||
|
||||
if (ISSUPP_11ACMURXBEAMFORMEE(cap))
|
||||
vht_cap |= IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE;
|
||||
|
||||
if (ISSUPP_11ACMUTXBEAMFORMEE(cap))
|
||||
vht_cap |= IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE;
|
||||
|
||||
if (ISSUPP_11ACSUBEAMFORMER(cap))
|
||||
vht_cap |= IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE;
|
||||
|
||||
if (ISSUPP_11ACSUBEAMFORMEE(cap))
|
||||
vht_cap |= IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE;
|
||||
|
||||
if (ISSUPP_11ACRXSTBC(cap))
|
||||
vht_cap |= IEEE80211_VHT_CAP_RXSTBC_1;
|
||||
|
||||
if (ISSUPP_11ACTXSTBC(cap))
|
||||
vht_cap |= IEEE80211_VHT_CAP_TXSTBC;
|
||||
|
||||
if (ISSUPP_11ACSGI160(cap))
|
||||
vht_cap |= IEEE80211_VHT_CAP_SHORT_GI_160;
|
||||
|
||||
if (ISSUPP_11ACSGI80(cap))
|
||||
vht_cap |= IEEE80211_VHT_CAP_SHORT_GI_80;
|
||||
|
||||
if (ISSUPP_11ACLDPC(cap))
|
||||
vht_cap |= IEEE80211_VHT_CAP_RXLDPC;
|
||||
|
||||
if (ISSUPP_11ACBW8080(cap))
|
||||
vht_cap |= IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ;
|
||||
|
||||
if (ISSUPP_11ACBW160(cap))
|
||||
vht_cap |= IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
|
||||
|
||||
vht_info->cap = vht_cap;
|
||||
|
||||
/* Update MCS support for VHT */
|
||||
vht_info->vht_mcs.rx_mcs_map = cpu_to_le16(
|
||||
adapter->hw_dot_11ac_mcs_support & 0xFFFF);
|
||||
vht_info->vht_mcs.rx_highest = 0;
|
||||
vht_info->vht_mcs.tx_mcs_map = cpu_to_le16(
|
||||
adapter->hw_dot_11ac_mcs_support >> 16);
|
||||
vht_info->vht_mcs.tx_highest = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function sets up the CFG802.11 specific HT capability fields
|
||||
* with default values.
|
||||
@@ -2092,11 +2214,18 @@ struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy,
|
||||
priv->netdev = dev;
|
||||
|
||||
mwifiex_setup_ht_caps(&wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap, priv);
|
||||
if (adapter->is_hw_11ac_capable)
|
||||
mwifiex_setup_vht_caps(
|
||||
&wiphy->bands[IEEE80211_BAND_2GHZ]->vht_cap, priv);
|
||||
|
||||
if (adapter->config_bands & BAND_A)
|
||||
mwifiex_setup_ht_caps(
|
||||
&wiphy->bands[IEEE80211_BAND_5GHZ]->ht_cap, priv);
|
||||
|
||||
if ((adapter->config_bands & BAND_A) && adapter->is_hw_11ac_capable)
|
||||
mwifiex_setup_vht_caps(
|
||||
&wiphy->bands[IEEE80211_BAND_5GHZ]->vht_cap, priv);
|
||||
|
||||
dev_net_set(dev, wiphy_net(wiphy));
|
||||
dev->ieee80211_ptr = priv->wdev;
|
||||
dev->ieee80211_ptr->iftype = priv->bss_mode;
|
||||
|
||||
@@ -106,8 +106,8 @@ u8 *mwifiex_11d_code_2_region(u8 code)
|
||||
* This function maps an index in supported rates table into
|
||||
* the corresponding data rate.
|
||||
*/
|
||||
u32 mwifiex_index_to_data_rate(struct mwifiex_private *priv, u8 index,
|
||||
u8 ht_info)
|
||||
u32 mwifiex_index_to_acs_data_rate(struct mwifiex_private *priv,
|
||||
u8 index, u8 ht_info)
|
||||
{
|
||||
/*
|
||||
* For every mcs_rate line, the first 8 bytes are for stream 1x1,
|
||||
@@ -130,10 +130,155 @@ u32 mwifiex_index_to_data_rate(struct mwifiex_private *priv, u8 index,
|
||||
{ 0x0e, 0x1c, 0x2b, 0x39, 0x56, 0x73, 0x82, 0x90,
|
||||
0x1c, 0x39, 0x56, 0x73, 0xad, 0xe7, 0x104, 0x120 }
|
||||
};
|
||||
/* AC rates */
|
||||
u16 ac_mcs_rate_nss1[8][10] = {
|
||||
/* LG 160M */
|
||||
{ 0x75, 0xEA, 0x15F, 0x1D4, 0x2BE, 0x3A8, 0x41D,
|
||||
0x492, 0x57C, 0x618 },
|
||||
|
||||
/* SG 160M */
|
||||
{ 0x82, 0x104, 0x186, 0x208, 0x30C, 0x410, 0x492,
|
||||
0x514, 0x618, 0x6C6 },
|
||||
|
||||
/* LG 80M */
|
||||
{ 0x3B, 0x75, 0xB0, 0xEA, 0x15F, 0x1D4, 0x20F,
|
||||
0x249, 0x2BE, 0x30C },
|
||||
|
||||
/* SG 80M */
|
||||
{ 0x41, 0x82, 0xC3, 0x104, 0x186, 0x208, 0x249,
|
||||
0x28A, 0x30C, 0x363 },
|
||||
|
||||
/* LG 40M */
|
||||
{ 0x1B, 0x36, 0x51, 0x6C, 0xA2, 0xD8, 0xF3,
|
||||
0x10E, 0x144, 0x168 },
|
||||
|
||||
/* SG 40M */
|
||||
{ 0x1E, 0x3C, 0x5A, 0x78, 0xB4, 0xF0, 0x10E,
|
||||
0x12C, 0x168, 0x190 },
|
||||
|
||||
/* LG 20M */
|
||||
{ 0xD, 0x1A, 0x27, 0x34, 0x4E, 0x68, 0x75, 0x82, 0x9C, 0x00 },
|
||||
|
||||
/* SG 20M */
|
||||
{ 0xF, 0x1D, 0x2C, 0x3A, 0x57, 0x74, 0x82, 0x91, 0xAE, 0x00 },
|
||||
};
|
||||
/* NSS2 note: the value in the table is 2 multiplier of the actual
|
||||
* rate
|
||||
*/
|
||||
u16 ac_mcs_rate_nss2[8][10] = {
|
||||
/* LG 160M */
|
||||
{ 0xEA, 0x1D4, 0x2BE, 0x3A8, 0x57C, 0x750, 0x83A,
|
||||
0x924, 0xAF8, 0xC30 },
|
||||
|
||||
/* SG 160M */
|
||||
{ 0x104, 0x208, 0x30C, 0x410, 0x618, 0x820, 0x924,
|
||||
0xA28, 0xC30, 0xD8B },
|
||||
|
||||
/* LG 80M */
|
||||
{ 0x75, 0xEA, 0x15F, 0x1D4, 0x2BE, 0x3A8, 0x41D,
|
||||
0x492, 0x57C, 0x618 },
|
||||
|
||||
/* SG 80M */
|
||||
{ 0x82, 0x104, 0x186, 0x208, 0x30C, 0x410, 0x492,
|
||||
0x514, 0x618, 0x6C6 },
|
||||
|
||||
/* LG 40M */
|
||||
{ 0x36, 0x6C, 0xA2, 0xD8, 0x144, 0x1B0, 0x1E6,
|
||||
0x21C, 0x288, 0x2D0 },
|
||||
|
||||
/* SG 40M */
|
||||
{ 0x3C, 0x78, 0xB4, 0xF0, 0x168, 0x1E0, 0x21C,
|
||||
0x258, 0x2D0, 0x320 },
|
||||
|
||||
/* LG 20M */
|
||||
{ 0x1A, 0x34, 0x4A, 0x68, 0x9C, 0xD0, 0xEA, 0x104,
|
||||
0x138, 0x00 },
|
||||
|
||||
/* SG 20M */
|
||||
{ 0x1D, 0x3A, 0x57, 0x74, 0xAE, 0xE6, 0x104, 0x121,
|
||||
0x15B, 0x00 },
|
||||
};
|
||||
u32 rate = 0;
|
||||
u8 mcs_index = 0;
|
||||
u8 bw = 0;
|
||||
u8 gi = 0;
|
||||
|
||||
if ((ht_info & 0x3) == MWIFIEX_RATE_FORMAT_VHT) {
|
||||
mcs_index = min(index & 0xF, 9);
|
||||
|
||||
/* 20M: bw=0, 40M: bw=1, 80M: bw=2, 160M: bw=3 */
|
||||
bw = (ht_info & 0xC) >> 2;
|
||||
|
||||
/* LGI: gi =0, SGI: gi = 1 */
|
||||
gi = (ht_info & 0x10) >> 4;
|
||||
|
||||
if ((index >> 4) == 1) /* NSS = 2 */
|
||||
rate = ac_mcs_rate_nss2[2 * (3 - bw) + gi][mcs_index];
|
||||
else /* NSS = 1 */
|
||||
rate = ac_mcs_rate_nss1[2 * (3 - bw) + gi][mcs_index];
|
||||
} else if ((ht_info & 0x3) == MWIFIEX_RATE_FORMAT_HT) {
|
||||
/* 20M: bw=0, 40M: bw=1 */
|
||||
bw = (ht_info & 0xC) >> 2;
|
||||
|
||||
/* LGI: gi =0, SGI: gi = 1 */
|
||||
gi = (ht_info & 0x10) >> 4;
|
||||
|
||||
if (index == MWIFIEX_RATE_BITMAP_MCS0) {
|
||||
if (gi == 1)
|
||||
rate = 0x0D; /* MCS 32 SGI rate */
|
||||
else
|
||||
rate = 0x0C; /* MCS 32 LGI rate */
|
||||
} else if (index < 16) {
|
||||
if ((bw == 1) || (bw == 0))
|
||||
rate = mcs_rate[2 * (1 - bw) + gi][index];
|
||||
else
|
||||
rate = mwifiex_data_rates[0];
|
||||
} else {
|
||||
rate = mwifiex_data_rates[0];
|
||||
}
|
||||
} else {
|
||||
/* 11n non-HT rates */
|
||||
if (index >= MWIFIEX_SUPPORTED_RATES_EXT)
|
||||
index = 0;
|
||||
rate = mwifiex_data_rates[index];
|
||||
}
|
||||
|
||||
return rate;
|
||||
}
|
||||
|
||||
/* This function maps an index in supported rates table into
|
||||
* the corresponding data rate.
|
||||
*/
|
||||
u32 mwifiex_index_to_data_rate(struct mwifiex_private *priv,
|
||||
u8 index, u8 ht_info)
|
||||
{
|
||||
/* For every mcs_rate line, the first 8 bytes are for stream 1x1,
|
||||
* and all 16 bytes are for stream 2x2.
|
||||
*/
|
||||
u16 mcs_rate[4][16] = {
|
||||
/* LGI 40M */
|
||||
{ 0x1b, 0x36, 0x51, 0x6c, 0xa2, 0xd8, 0xf3, 0x10e,
|
||||
0x36, 0x6c, 0xa2, 0xd8, 0x144, 0x1b0, 0x1e6, 0x21c },
|
||||
|
||||
/* SGI 40M */
|
||||
{ 0x1e, 0x3c, 0x5a, 0x78, 0xb4, 0xf0, 0x10e, 0x12c,
|
||||
0x3c, 0x78, 0xb4, 0xf0, 0x168, 0x1e0, 0x21c, 0x258 },
|
||||
|
||||
/* LGI 20M */
|
||||
{ 0x0d, 0x1a, 0x27, 0x34, 0x4e, 0x68, 0x75, 0x82,
|
||||
0x1a, 0x34, 0x4e, 0x68, 0x9c, 0xd0, 0xea, 0x104 },
|
||||
|
||||
/* SGI 20M */
|
||||
{ 0x0e, 0x1c, 0x2b, 0x39, 0x56, 0x73, 0x82, 0x90,
|
||||
0x1c, 0x39, 0x56, 0x73, 0xad, 0xe7, 0x104, 0x120 }
|
||||
};
|
||||
u32 mcs_num_supp =
|
||||
(priv->adapter->hw_dev_mcs_support == HT_STREAM_2X2) ? 16 : 8;
|
||||
u32 rate;
|
||||
|
||||
if (priv->adapter->is_hw_11ac_capable)
|
||||
return mwifiex_index_to_acs_data_rate(priv, index, ht_info);
|
||||
|
||||
if (ht_info & BIT(0)) {
|
||||
if (index == MWIFIEX_RATE_BITMAP_MCS0) {
|
||||
if (ht_info & BIT(2))
|
||||
@@ -269,6 +414,7 @@ u32 mwifiex_get_supported_rates(struct mwifiex_private *priv, u8 *rates)
|
||||
{
|
||||
u32 k = 0;
|
||||
struct mwifiex_adapter *adapter = priv->adapter;
|
||||
|
||||
if (priv->bss_mode == NL80211_IFTYPE_STATION) {
|
||||
switch (adapter->config_bands) {
|
||||
case BAND_B:
|
||||
@@ -279,6 +425,7 @@ u32 mwifiex_get_supported_rates(struct mwifiex_private *priv, u8 *rates)
|
||||
break;
|
||||
case BAND_G:
|
||||
case BAND_G | BAND_GN:
|
||||
case BAND_G | BAND_GN | BAND_GAC:
|
||||
dev_dbg(adapter->dev, "info: infra band=%d "
|
||||
"supported_rates_g\n", adapter->config_bands);
|
||||
k = mwifiex_copy_rates(rates, k, supported_rates_g,
|
||||
@@ -288,7 +435,11 @@ u32 mwifiex_get_supported_rates(struct mwifiex_private *priv, u8 *rates)
|
||||
case BAND_A | BAND_B | BAND_G:
|
||||
case BAND_A | BAND_B:
|
||||
case BAND_A | BAND_B | BAND_G | BAND_GN | BAND_AN:
|
||||
case BAND_A | BAND_B | BAND_G | BAND_GN | BAND_AN | BAND_AAC:
|
||||
case BAND_A | BAND_B | BAND_G | BAND_GN | BAND_AN |
|
||||
BAND_AAC | BAND_GAC:
|
||||
case BAND_B | BAND_G | BAND_GN:
|
||||
case BAND_B | BAND_G | BAND_GN | BAND_GAC:
|
||||
dev_dbg(adapter->dev, "info: infra band=%d "
|
||||
"supported_rates_bg\n", adapter->config_bands);
|
||||
k = mwifiex_copy_rates(rates, k, supported_rates_bg,
|
||||
@@ -301,14 +452,18 @@ u32 mwifiex_get_supported_rates(struct mwifiex_private *priv, u8 *rates)
|
||||
k = mwifiex_copy_rates(rates, k, supported_rates_a,
|
||||
sizeof(supported_rates_a));
|
||||
break;
|
||||
case BAND_AN:
|
||||
case BAND_A | BAND_AN:
|
||||
case BAND_A | BAND_AN | BAND_AAC:
|
||||
case BAND_A | BAND_G | BAND_AN | BAND_GN:
|
||||
case BAND_A | BAND_G | BAND_AN | BAND_GN | BAND_AAC:
|
||||
dev_dbg(adapter->dev, "info: infra band=%d "
|
||||
"supported_rates_a\n", adapter->config_bands);
|
||||
k = mwifiex_copy_rates(rates, k, supported_rates_a,
|
||||
sizeof(supported_rates_a));
|
||||
break;
|
||||
case BAND_GN:
|
||||
case BAND_GN | BAND_GAC:
|
||||
dev_dbg(adapter->dev, "info: infra band=%d "
|
||||
"supported_rates_n\n", adapter->config_bands);
|
||||
k = mwifiex_copy_rates(rates, k, supported_rates_n,
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "main.h"
|
||||
#include "wmm.h"
|
||||
#include "11n.h"
|
||||
#include "11ac.h"
|
||||
|
||||
/*
|
||||
* This function initializes a command node.
|
||||
@@ -1465,6 +1466,24 @@ int mwifiex_ret_get_hw_spec(struct mwifiex_private *priv,
|
||||
adapter->fw_release_number = le32_to_cpu(hw_spec->fw_release_number);
|
||||
adapter->number_of_antenna = le16_to_cpu(hw_spec->number_of_antenna);
|
||||
|
||||
if (le32_to_cpu(hw_spec->dot_11ac_dev_cap)) {
|
||||
adapter->is_hw_11ac_capable = true;
|
||||
|
||||
/* Copy 11AC cap */
|
||||
adapter->hw_dot_11ac_dev_cap =
|
||||
le32_to_cpu(hw_spec->dot_11ac_dev_cap);
|
||||
adapter->usr_dot_11ac_dev_cap_bg = adapter->hw_dot_11ac_dev_cap;
|
||||
adapter->usr_dot_11ac_dev_cap_a = adapter->hw_dot_11ac_dev_cap;
|
||||
|
||||
/* Copy 11AC mcs */
|
||||
adapter->hw_dot_11ac_mcs_support =
|
||||
le32_to_cpu(hw_spec->dot_11ac_mcs_support);
|
||||
adapter->usr_dot_11ac_mcs_support =
|
||||
adapter->hw_dot_11ac_mcs_support;
|
||||
} else {
|
||||
adapter->is_hw_11ac_capable = false;
|
||||
}
|
||||
|
||||
dev_dbg(adapter->dev, "info: GET_HW_SPEC: fw_release_number- %#x\n",
|
||||
adapter->fw_release_number);
|
||||
dev_dbg(adapter->dev, "info: GET_HW_SPEC: permanent addr: %pM\n",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user