static void iwl_mvm_tt_tx_protection(struct iwl_mvm *mvm, bool enable) { struct ieee80211_sta *sta; struct iwl_mvm_sta *mvmsta; int i, err; for (i = 0; i < IWL_MVM_STATION_COUNT; i++) { sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i], lockdep_is_held(&mvm->mutex)); if (IS_ERR_OR_NULL(sta)) continue; mvmsta = iwl_mvm_sta_from_mac80211(sta); if (enable == mvmsta->tt_tx_protection) continue; err = iwl_mvm_tx_protection(mvm, mvmsta, enable); if (err) { IWL_ERR(mvm, "Failed to %s Tx protection\n", enable ? "enable" : "disable"); } else { IWL_DEBUG_TEMP(mvm, "%s Tx protection\n", enable ? "Enable" : "Disable"); mvmsta->tt_tx_protection = enable; } } }
int iwl_mvm_tdls_sta_count(struct iwl_mvm *mvm, struct ieee80211_vif *vif) { struct ieee80211_sta *sta; struct iwl_mvm_sta *mvmsta; int count = 0; int i; lockdep_assert_held(&mvm->mutex); for (i = 0; i < ARRAY_SIZE(mvm->fw_id_to_mac_id); i++) { sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i], lockdep_is_held(&mvm->mutex)); if (!sta || IS_ERR(sta) || !sta->tdls) continue; if (vif) { mvmsta = iwl_mvm_sta_from_mac80211(sta); if (mvmsta->vif != vif) continue; } count++; } return count; }
/* * returns true if a packet outside BA session is a duplicate and * should be dropped */ static bool iwl_mvm_is_nonagg_dup(struct ieee80211_sta *sta, int queue, struct ieee80211_rx_status *rx_status, struct ieee80211_hdr *hdr, struct iwl_rx_mpdu_desc *desc) { struct iwl_mvm_sta *mvm_sta; struct iwl_mvm_rxq_dup_data *dup_data; u8 baid, tid, sub_frame_idx; if (WARN_ON(IS_ERR_OR_NULL(sta))) return false; baid = (le32_to_cpu(desc->reorder_data) & IWL_RX_MPDU_REORDER_BAID_MASK) >> IWL_RX_MPDU_REORDER_BAID_SHIFT; if (baid != IWL_RX_REORDER_DATA_INVALID_BAID) return false; mvm_sta = iwl_mvm_sta_from_mac80211(sta); dup_data = &mvm_sta->dup_data[queue]; /* * Drop duplicate 802.11 retransmissions * (IEEE 802.11-2012: 9.3.2.10 "Duplicate detection and recovery") */ if (ieee80211_is_ctl(hdr->frame_control) || ieee80211_is_qos_nullfunc(hdr->frame_control) || is_multicast_ether_addr(hdr->addr1)) { rx_status->flag |= RX_FLAG_DUP_VALIDATED; return false; } if (ieee80211_is_data_qos(hdr->frame_control)) /* frame has qos control */ tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK; else tid = IWL_MAX_TID_COUNT; /* If this wasn't a part of an A-MSDU the sub-frame index will be 0 */ sub_frame_idx = desc->amsdu_info & IWL_RX_MPDU_AMSDU_SUBFRAME_IDX_MASK; if (unlikely(ieee80211_has_retry(hdr->frame_control) && dup_data->last_seq[tid] == hdr->seq_ctrl && dup_data->last_sub_frame[tid] >= sub_frame_idx)) return true; dup_data->last_seq[tid] = hdr->seq_ctrl; dup_data->last_sub_frame[tid] = sub_frame_idx; rx_status->flag |= RX_FLAG_DUP_VALIDATED; return false; }
static void iwl_mvm_rx_csum(struct ieee80211_sta *sta, struct sk_buff *skb, struct iwl_rx_mpdu_desc *desc) { struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(mvmsta->vif); if (mvmvif->features & NETIF_F_RXCSUM && desc->l3l4_flags & cpu_to_le16(IWL_RX_L3L4_IP_HDR_CSUM_OK) && desc->l3l4_flags & cpu_to_le16(IWL_RX_L3L4_TCP_UDP_CSUM_OK)) skb->ip_summed = CHECKSUM_UNNECESSARY; }
static void iwl_mvm_rx_handle_tcm(struct iwl_mvm *mvm, struct ieee80211_sta *sta, struct ieee80211_hdr *hdr, u32 len, struct iwl_rx_phy_info *phy_info, u32 rate_n_flags) { struct iwl_mvm_sta *mvmsta; struct iwl_mvm_tcm_mac *mdata; int mac; int ac = IEEE80211_AC_BE; /* treat non-QoS as BE */ struct iwl_mvm_vif *mvmvif; /* expected throughput in 100Kbps, single stream, 20 MHz */ static const u8 thresh_tpt[] = { 9, 18, 30, 42, 60, 78, 90, 96, 120, 135, }; u16 thr; if (ieee80211_is_data_qos(hdr->frame_control)) ac = tid_to_mac80211_ac[ieee80211_get_tid(hdr)]; mvmsta = iwl_mvm_sta_from_mac80211(sta); mac = mvmsta->mac_id_n_color & FW_CTXT_ID_MSK; if (time_after(jiffies, mvm->tcm.ts + MVM_TCM_PERIOD)) schedule_delayed_work(&mvm->tcm.work, 0); mdata = &mvm->tcm.data[mac]; mdata->rx.pkts[ac]++; /* count the airtime only once for each ampdu */ if (mdata->rx.last_ampdu_ref != mvm->ampdu_ref) { mdata->rx.last_ampdu_ref = mvm->ampdu_ref; mdata->rx.airtime += le16_to_cpu(phy_info->frame_time); } if (!(rate_n_flags & (RATE_MCS_HT_MSK | RATE_MCS_VHT_MSK))) return; mvmvif = iwl_mvm_vif_from_mac80211(mvmsta->vif); if (mdata->opened_rx_ba_sessions || mdata->uapsd_nonagg_detect.detected || (!mvmvif->queue_params[IEEE80211_AC_VO].uapsd && !mvmvif->queue_params[IEEE80211_AC_VI].uapsd && !mvmvif->queue_params[IEEE80211_AC_BE].uapsd && !mvmvif->queue_params[IEEE80211_AC_BK].uapsd) || mvmsta->sta_id != mvmvif->ap_sta_id) return; if (rate_n_flags & RATE_MCS_HT_MSK) { thr = thresh_tpt[rate_n_flags & RATE_HT_MCS_RATE_CODE_MSK]; thr *= 1 + ((rate_n_flags & RATE_HT_MCS_NSS_MSK) >> RATE_HT_MCS_NSS_POS); } else {
static void iwl_mvm_rx_csum(struct ieee80211_sta *sta, struct sk_buff *skb, u32 status) { struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(mvmsta->vif); if (mvmvif->features & NETIF_F_RXCSUM && status & RX_MPDU_RES_STATUS_CSUM_DONE && status & RX_MPDU_RES_STATUS_CSUM_OK) skb->ip_summed = CHECKSUM_UNNECESSARY; }
static void iwl_mvm_check_ratid_empty(struct iwl_mvm *mvm, struct ieee80211_sta *sta, u8 tid) { struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid]; struct ieee80211_vif *vif = mvmsta->vif; lockdep_assert_held(&mvmsta->lock); if ((tid_data->state == IWL_AGG_ON || tid_data->state == IWL_EMPTYING_HW_QUEUE_DELBA) && iwl_mvm_tid_queued(tid_data) == 0) { /* * Now that this aggregation queue is empty tell mac80211 so it * knows we no longer have frames buffered for the station on * this TID (for the TIM bitmap calculation.) */ ieee80211_sta_set_buffered(sta, tid, false); } if (tid_data->ssn != tid_data->next_reclaimed) return; switch (tid_data->state) { case IWL_EMPTYING_HW_QUEUE_ADDBA: IWL_DEBUG_TX_QUEUES(mvm, "Can continue addBA flow ssn = next_recl = %d\n", tid_data->next_reclaimed); tid_data->state = IWL_AGG_STARTING; ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; case IWL_EMPTYING_HW_QUEUE_DELBA: IWL_DEBUG_TX_QUEUES(mvm, "Can continue DELBA flow ssn = next_recl = %d\n", tid_data->next_reclaimed); iwl_mvm_disable_txq(mvm, tid_data->txq_id); tid_data->state = IWL_AGG_OFF; /* * we can't hold the mutex - but since we are after a sequence * point (call to iwl_mvm_disable_txq(), so we don't even need * a memory barrier. */ mvm->queue_to_mac80211[tid_data->txq_id] = IWL_INVALID_MAC80211_QUEUE; ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; default: break; } }
static void iwl_mvm_rx_csum(struct ieee80211_sta *sta, struct sk_buff *skb, struct iwl_rx_mpdu_desc *desc) { struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(mvmsta->vif); u16 flags = le16_to_cpu(desc->l3l4_flags); u8 l3_prot = (u8)((flags & IWL_RX_L3L4_L3_PROTO_MASK) >> IWL_RX_L3_PROTO_POS); if (mvmvif->features & NETIF_F_RXCSUM && flags & IWL_RX_L3L4_TCP_UDP_CSUM_OK && (flags & IWL_RX_L3L4_IP_HDR_CSUM_OK || l3_prot == IWL_RX_L3_TYPE_IPV6 || l3_prot == IWL_RX_L3_TYPE_IPV6_FRAG)) skb->ip_summed = CHECKSUM_UNNECESSARY; }
static void iwl_mvm_check_ratid_empty(struct iwl_mvm *mvm, struct ieee80211_sta *sta, u8 tid) { struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid]; struct ieee80211_vif *vif = mvmsta->vif; lockdep_assert_held(&mvmsta->lock); if (tid_data->ssn != tid_data->next_reclaimed) return; switch (tid_data->state) { case IWL_EMPTYING_HW_QUEUE_ADDBA: IWL_DEBUG_TX_QUEUES(mvm, "Can continue addBA flow ssn = next_recl = %d\n", tid_data->next_reclaimed); tid_data->state = IWL_AGG_STARTING; ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; case IWL_EMPTYING_HW_QUEUE_DELBA: IWL_DEBUG_TX_QUEUES(mvm, "Can continue DELBA flow ssn = next_recl = %d\n", tid_data->next_reclaimed); iwl_trans_txq_disable(mvm->trans, tid_data->txq_id); tid_data->state = IWL_AGG_OFF; /* * we can't hold the mutex - but since we are after a sequence * point (call to iwl_trans_txq_disable), so we don't even need * a memory barrier. */ mvm->queue_to_mac80211[tid_data->txq_id] = IWL_INVALID_MAC80211_QUEUE; ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; default: break; } }
void iwl_mvm_teardown_tdls_peers(struct iwl_mvm *mvm) { struct ieee80211_sta *sta; struct iwl_mvm_sta *mvmsta; int i; lockdep_assert_held(&mvm->mutex); for (i = 0; i < ARRAY_SIZE(mvm->fw_id_to_mac_id); i++) { sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i], lockdep_is_held(&mvm->mutex)); if (!sta || IS_ERR(sta) || !sta->tdls) continue; mvmsta = iwl_mvm_sta_from_mac80211(sta); ieee80211_tdls_oper_request(mvmsta->vif, sta->addr, NL80211_TDLS_TEARDOWN, WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED, GFP_KERNEL); } }
/* * iwl_mvm_rx_rx_mpdu - REPLY_RX_MPDU_CMD handler * * Handles the actual data of the Rx packet from the fw */ int iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, struct iwl_device_cmd *cmd) { struct ieee80211_hdr *hdr; struct ieee80211_rx_status *rx_status; struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_rx_phy_info *phy_info; struct iwl_rx_mpdu_res_start *rx_res; struct ieee80211_sta *sta; struct sk_buff *skb; u32 len; u32 ampdu_status; u32 rate_n_flags; u32 rx_pkt_status; u8 crypt_len = 0; phy_info = &mvm->last_phy_info; rx_res = (struct iwl_rx_mpdu_res_start *)pkt->data; hdr = (struct ieee80211_hdr *)(pkt->data + sizeof(*rx_res)); len = le16_to_cpu(rx_res->byte_count); rx_pkt_status = le32_to_cpup((__le32 *) (pkt->data + sizeof(*rx_res) + len)); /* Dont use dev_alloc_skb(), we'll have enough headroom once * ieee80211_hdr pulled. */ skb = alloc_skb(128, GFP_ATOMIC); if (!skb) { IWL_ERR(mvm, "alloc_skb failed\n"); return 0; } rx_status = IEEE80211_SKB_RXCB(skb); /* * drop the packet if it has failed being decrypted by HW */ if (iwl_mvm_set_mac80211_rx_flag(mvm, hdr, rx_status, rx_pkt_status, &crypt_len)) { IWL_DEBUG_DROP(mvm, "Bad decryption results 0x%08x\n", rx_pkt_status); kfree_skb(skb); return 0; } if ((unlikely(phy_info->cfg_phy_cnt > 20))) { IWL_DEBUG_DROP(mvm, "dsp size out of range [0,20]: %d\n", phy_info->cfg_phy_cnt); kfree_skb(skb); return 0; } /* * Keep packets with CRC errors (and with overrun) for monitor mode * (otherwise the firmware discards them) but mark them as bad. */ if (!(rx_pkt_status & RX_MPDU_RES_STATUS_CRC_OK) || !(rx_pkt_status & RX_MPDU_RES_STATUS_OVERRUN_OK)) { IWL_DEBUG_RX(mvm, "Bad CRC or FIFO: 0x%08X.\n", rx_pkt_status); rx_status->flag |= RX_FLAG_FAILED_FCS_CRC; } /* This will be used in several places later */ rate_n_flags = le32_to_cpu(phy_info->rate_n_flags); /* 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; rx_status->freq = ieee80211_channel_to_frequency(le16_to_cpu(phy_info->channel), rx_status->band); /* * TSF as indicated by the fw is at INA time, but mac80211 expects the * TSF at the beginning of the MPDU. */ /*rx_status->flag |= RX_FLAG_MACTIME_MPDU;*/ iwl_mvm_get_signal_strength(mvm, phy_info, rx_status); IWL_DEBUG_STATS_LIMIT(mvm, "Rssi %d, TSF %llu\n", rx_status->signal, (unsigned long long)rx_status->mactime); rcu_read_lock(); /* * We have tx blocked stations (with CS bit). If we heard frames from * a blocked station on a new channel we can TX to it again. */ if (unlikely(mvm->csa_tx_block_bcn_timeout)) { sta = ieee80211_find_sta( rcu_dereference(mvm->csa_tx_blocked_vif), hdr->addr2); if (sta) iwl_mvm_sta_modify_disable_tx_ap(mvm, sta, false); } /* This is fine since we don't support multiple AP interfaces */ sta = ieee80211_find_sta_by_ifaddr(mvm->hw, hdr->addr2, NULL); if (sta) { struct iwl_mvm_sta *mvmsta; mvmsta = iwl_mvm_sta_from_mac80211(sta); rs_update_last_rssi(mvm, &mvmsta->lq_sta, rx_status); if (iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_RSSI) && ieee80211_is_beacon(hdr->frame_control)) { struct iwl_fw_dbg_trigger_tlv *trig; struct iwl_fw_dbg_trigger_low_rssi *rssi_trig; bool trig_check; s32 rssi; trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_RSSI); rssi_trig = (void *)trig->data; rssi = le32_to_cpu(rssi_trig->rssi); trig_check = iwl_fw_dbg_trigger_check_stop(mvm, mvmsta->vif, trig); if (trig_check && rx_status->signal < rssi) iwl_mvm_fw_dbg_collect_trig(mvm, trig, NULL); } } rcu_read_unlock(); /* set the preamble flag if appropriate */ if (phy_info->phy_flags & cpu_to_le16(RX_RES_PHY_FLAGS_SHORT_PREAMBLE)) rx_status->flag |= RX_FLAG_SHORTPRE; if (phy_info->phy_flags & cpu_to_le16(RX_RES_PHY_FLAGS_AGG)) { /* * We know which subframes of an A-MPDU belong * together since we get a single PHY response * from the firmware for all of them */ rx_status->flag |= RX_FLAG_AMPDU_DETAILS; rx_status->ampdu_reference = mvm->ampdu_ref; } /* Set up the HT phy flags */ switch (rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK) { case RATE_MCS_CHAN_WIDTH_20: break; case RATE_MCS_CHAN_WIDTH_40: rx_status->flag |= RX_FLAG_40MHZ; break; case RATE_MCS_CHAN_WIDTH_80: rx_status->vht_flag |= RX_VHT_FLAG_80MHZ; break; case RATE_MCS_CHAN_WIDTH_160: rx_status->vht_flag |= RX_VHT_FLAG_160MHZ; break; } if (rate_n_flags & RATE_MCS_SGI_MSK) rx_status->flag |= RX_FLAG_SHORT_GI; if (rate_n_flags & RATE_HT_MCS_GF_MSK) rx_status->flag |= RX_FLAG_HT_GF; if (rate_n_flags & RATE_MCS_LDPC_MSK) rx_status->flag |= RX_FLAG_LDPC; if (rate_n_flags & RATE_MCS_HT_MSK) { u8 stbc = (rate_n_flags & RATE_MCS_HT_STBC_MSK) >> RATE_MCS_STBC_POS; rx_status->flag |= RX_FLAG_HT; rx_status->rate_idx = rate_n_flags & RATE_HT_MCS_INDEX_MSK; rx_status->flag |= stbc << RX_FLAG_STBC_SHIFT; } else if (rate_n_flags & RATE_MCS_VHT_MSK) {
static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm) { struct iwl_bt_iterator_data data = { .mvm = mvm, .notif = &mvm->last_bt_notif, }; struct iwl_bt_coex_ci_cmd cmd = {}; u8 ci_bw_idx; /* Ignore updates if we are in force mode */ if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS)) return; rcu_read_lock(); ieee80211_iterate_active_interfaces_atomic( mvm->hw, IEEE80211_IFACE_ITER_NORMAL, iwl_mvm_bt_notif_iterator, &data); iwl_mvm_bt_coex_tcm_based_ci(mvm, &data); if (data.primary) { struct ieee80211_chanctx_conf *chan = data.primary; if (WARN_ON(!chan->def.chan)) { rcu_read_unlock(); return; } if (chan->def.width < NL80211_CHAN_WIDTH_40) { ci_bw_idx = 0; } else { if (chan->def.center_freq1 > chan->def.chan->center_freq) ci_bw_idx = 2; else ci_bw_idx = 1; } cmd.bt_primary_ci = iwl_ci_mask[chan->def.chan->hw_value][ci_bw_idx]; cmd.primary_ch_phy_id = cpu_to_le32(*((u16 *)data.primary->drv_priv)); } if (data.secondary) { struct ieee80211_chanctx_conf *chan = data.secondary; if (WARN_ON(!data.secondary->def.chan)) { rcu_read_unlock(); return; } if (chan->def.width < NL80211_CHAN_WIDTH_40) { ci_bw_idx = 0; } else { if (chan->def.center_freq1 > chan->def.chan->center_freq) ci_bw_idx = 2; else ci_bw_idx = 1; } cmd.bt_secondary_ci = iwl_ci_mask[chan->def.chan->hw_value][ci_bw_idx]; cmd.secondary_ch_phy_id = cpu_to_le32(*((u16 *)data.secondary->drv_priv)); } rcu_read_unlock(); /* Don't spam the fw with the same command over and over */ if (memcmp(&cmd, &mvm->last_bt_ci_cmd, sizeof(cmd))) { if (iwl_mvm_send_cmd_pdu(mvm, BT_COEX_CI, 0, sizeof(cmd), &cmd)) IWL_ERR(mvm, "Failed to send BT_CI cmd\n"); memcpy(&mvm->last_bt_ci_cmd, &cmd, sizeof(cmd)); } } void iwl_mvm_rx_bt_coex_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) { struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_bt_coex_profile_notif *notif = (void *)pkt->data; IWL_DEBUG_COEX(mvm, "BT Coex Notification received\n"); IWL_DEBUG_COEX(mvm, "\tBT ci compliance %d\n", notif->bt_ci_compliance); IWL_DEBUG_COEX(mvm, "\tBT primary_ch_lut %d\n", le32_to_cpu(notif->primary_ch_lut)); IWL_DEBUG_COEX(mvm, "\tBT secondary_ch_lut %d\n", le32_to_cpu(notif->secondary_ch_lut)); IWL_DEBUG_COEX(mvm, "\tBT activity grading %d\n", le32_to_cpu(notif->bt_activity_grading)); /* remember this notification for future use: rssi fluctuations */ memcpy(&mvm->last_bt_notif, notif, sizeof(mvm->last_bt_notif)); iwl_mvm_bt_coex_notif_handle(mvm); } void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif, enum ieee80211_rssi_event_data rssi_event) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); int ret; lockdep_assert_held(&mvm->mutex); /* Ignore updates if we are in force mode */ if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS)) return; /* * Rssi update while not associated - can happen since the statistics * are handled asynchronously */ if (mvmvif->ap_sta_id == IWL_MVM_INVALID_STA) return; /* No BT - reports should be disabled */ if (le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) == BT_OFF) return; IWL_DEBUG_COEX(mvm, "RSSI for %pM is now %s\n", vif->bss_conf.bssid, rssi_event == RSSI_EVENT_HIGH ? "HIGH" : "LOW"); /* * Check if rssi is good enough for reduced Tx power, but not in loose * scheme. */ if (rssi_event == RSSI_EVENT_LOW || mvm->cfg->bt_shared_single_ant || iwl_get_coex_type(mvm, vif) == BT_COEX_LOOSE_LUT) ret = iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, false); else ret = iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, true); if (ret) IWL_ERR(mvm, "couldn't send BT_CONFIG HCMD upon RSSI event\n"); } #define LINK_QUAL_AGG_TIME_LIMIT_DEF (4000) #define LINK_QUAL_AGG_TIME_LIMIT_BT_ACT (1200) u16 iwl_mvm_coex_agg_time_limit(struct iwl_mvm *mvm, struct ieee80211_sta *sta) { struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(mvmsta->vif); struct iwl_mvm_phy_ctxt *phy_ctxt = mvmvif->phy_ctxt; enum iwl_bt_coex_lut_type lut_type; if (mvm->last_bt_notif.ttc_status & BIT(phy_ctxt->id)) return LINK_QUAL_AGG_TIME_LIMIT_DEF; if (le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) < BT_HIGH_TRAFFIC) return LINK_QUAL_AGG_TIME_LIMIT_DEF; lut_type = iwl_get_coex_type(mvm, mvmsta->vif); if (lut_type == BT_COEX_LOOSE_LUT || lut_type == BT_COEX_INVALID_LUT) return LINK_QUAL_AGG_TIME_LIMIT_DEF; /* tight coex, high bt traffic, reduce AGG time limit */ return LINK_QUAL_AGG_TIME_LIMIT_BT_ACT; } bool iwl_mvm_bt_coex_is_mimo_allowed(struct iwl_mvm *mvm, struct ieee80211_sta *sta) { struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(mvmsta->vif); struct iwl_mvm_phy_ctxt *phy_ctxt = mvmvif->phy_ctxt; enum iwl_bt_coex_lut_type lut_type; if (mvm->last_bt_notif.ttc_status & BIT(phy_ctxt->id)) return true; if (le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) < BT_HIGH_TRAFFIC) return true; /* * In Tight / TxTxDis, BT can't Rx while we Tx, so use both antennas * since BT is already killed. * In Loose, BT can Rx while we Tx, so forbid MIMO to let BT Rx while * we Tx. * When we are in 5GHz, we'll get BT_COEX_INVALID_LUT allowing MIMO. */ lut_type = iwl_get_coex_type(mvm, mvmsta->vif); return lut_type != BT_COEX_LOOSE_LUT; } bool iwl_mvm_bt_coex_is_ant_avail(struct iwl_mvm *mvm, u8 ant) { /* there is no other antenna, shared antenna is always available */ if (mvm->cfg->bt_shared_single_ant) return true; if (ant & mvm->cfg->non_shared_ant) return true; return le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) < BT_HIGH_TRAFFIC; } bool iwl_mvm_bt_coex_is_shared_ant_avail(struct iwl_mvm *mvm) { /* there is no other antenna, shared antenna is always available */ if (mvm->cfg->bt_shared_single_ant) return true; return le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) < BT_HIGH_TRAFFIC; } bool iwl_mvm_bt_coex_is_tpc_allowed(struct iwl_mvm *mvm, enum nl80211_band band) { u32 bt_activity = le32_to_cpu(mvm->last_bt_notif.bt_activity_grading); if (band != NL80211_BAND_2GHZ) return false; return bt_activity >= BT_LOW_TRAFFIC; }
static ssize_t iwl_dbgfs_mac_params_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct ieee80211_vif *vif = file->private_data; struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct iwl_mvm *mvm = mvmvif->mvm; u8 ap_sta_id; struct ieee80211_chanctx_conf *chanctx_conf; char buf[512]; int bufsz = sizeof(buf); int pos = 0; int i; mutex_lock(&mvm->mutex); ap_sta_id = mvmvif->ap_sta_id; switch (ieee80211_vif_type_p2p(vif)) { case NL80211_IFTYPE_ADHOC: pos += scnprintf(buf+pos, bufsz-pos, "type: ibss\n"); break; case NL80211_IFTYPE_STATION: pos += scnprintf(buf+pos, bufsz-pos, "type: bss\n"); break; case NL80211_IFTYPE_AP: pos += scnprintf(buf+pos, bufsz-pos, "type: ap\n"); break; case NL80211_IFTYPE_P2P_CLIENT: pos += scnprintf(buf+pos, bufsz-pos, "type: p2p client\n"); break; case NL80211_IFTYPE_P2P_GO: pos += scnprintf(buf+pos, bufsz-pos, "type: p2p go\n"); break; case NL80211_IFTYPE_P2P_DEVICE: pos += scnprintf(buf+pos, bufsz-pos, "type: p2p dev\n"); break; default: break; } pos += scnprintf(buf+pos, bufsz-pos, "mac id/color: %d / %d\n", mvmvif->id, mvmvif->color); pos += scnprintf(buf+pos, bufsz-pos, "bssid: %pM\n", vif->bss_conf.bssid); pos += scnprintf(buf+pos, bufsz-pos, "QoS:\n"); for (i = 0; i < ARRAY_SIZE(mvmvif->queue_params); i++) pos += scnprintf(buf+pos, bufsz-pos, "\t%d: txop:%d - cw_min:%d - cw_max = %d - aifs = %d upasd = %d\n", i, mvmvif->queue_params[i].txop, mvmvif->queue_params[i].cw_min, mvmvif->queue_params[i].cw_max, mvmvif->queue_params[i].aifs, mvmvif->queue_params[i].uapsd); if (vif->type == NL80211_IFTYPE_STATION && ap_sta_id != IWL_MVM_STATION_COUNT) { struct ieee80211_sta *sta; sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[ap_sta_id], lockdep_is_held(&mvm->mutex)); if (!IS_ERR_OR_NULL(sta)) { struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); pos += scnprintf(buf+pos, bufsz-pos, "ap_sta_id %d - reduced Tx power %d\n", ap_sta_id, mvm_sta->bt_reduced_txpower); } } rcu_read_lock(); chanctx_conf = rcu_dereference(vif->chanctx_conf); if (chanctx_conf) pos += scnprintf(buf+pos, bufsz-pos, "idle rx chains %d, active rx chains: %d\n", chanctx_conf->rx_chains_static, chanctx_conf->rx_chains_dynamic); rcu_read_unlock(); mutex_unlock(&mvm->mutex); return simple_read_from_buffer(user_buf, count, ppos, buf, pos); }
/* * Sets the fields in the Tx cmd that are crypto related */ int iwl_mvm_tx_skb(struct iwl_mvm *mvm, struct sk_buff *skb, struct ieee80211_sta *sta) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct iwl_mvm_sta *mvmsta; struct iwl_device_cmd *dev_cmd; struct iwl_tx_cmd *tx_cmd; __le16 fc; u16 seq_number = 0; u8 tid = IWL_MAX_TID_COUNT; u8 txq_id = info->hw_queue; bool is_data_qos = false, is_ampdu = false; mvmsta = iwl_mvm_sta_from_mac80211(sta); fc = hdr->frame_control; if (WARN_ON_ONCE(!mvmsta)) return -1; if (WARN_ON_ONCE(mvmsta->sta_id == IWL_MVM_STATION_COUNT)) return -1; dev_cmd = iwl_mvm_set_tx_params(mvm, skb, sta, mvmsta->sta_id); if (!dev_cmd) goto drop; tx_cmd = (struct iwl_tx_cmd *)dev_cmd->payload; /* From now on, we cannot access info->control */ /* * we handle that entirely ourselves -- for uAPSD the firmware * will always send a notification, and for PS-Poll responses * we'll notify mac80211 when getting frame status */ info->flags &= ~IEEE80211_TX_STATUS_EOSP; spin_lock(&mvmsta->lock); if (ieee80211_is_data_qos(fc) && !ieee80211_is_qos_nullfunc(fc)) { u8 *qc = NULL; qc = ieee80211_get_qos_ctl(hdr); tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK; if (WARN_ON_ONCE(tid >= IWL_MAX_TID_COUNT)) goto drop_unlock_sta; seq_number = mvmsta->tid_data[tid].seq_number; seq_number &= IEEE80211_SCTL_SEQ; hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG); hdr->seq_ctrl |= cpu_to_le16(seq_number); is_data_qos = true; is_ampdu = info->flags & IEEE80211_TX_CTL_AMPDU; } /* Copy MAC header from skb into command buffer */ memcpy(tx_cmd->hdr, hdr, ieee80211_hdrlen(fc)); WARN_ON_ONCE(info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM); if (is_ampdu) { if (WARN_ON_ONCE(mvmsta->tid_data[tid].state != IWL_AGG_ON)) goto drop_unlock_sta; txq_id = mvmsta->tid_data[tid].txq_id; } IWL_DEBUG_TX(mvm, "TX to [%d|%d] Q:%d - seq: 0x%x\n", mvmsta->sta_id, tid, txq_id, IEEE80211_SEQ_TO_SN(seq_number)); if (iwl_trans_tx(mvm->trans, skb, dev_cmd, txq_id)) goto drop_unlock_sta; if (is_data_qos && !ieee80211_has_morefrags(fc)) mvmsta->tid_data[tid].seq_number = seq_number + 0x10; spin_unlock(&mvmsta->lock); if (txq_id < mvm->first_agg_queue) atomic_inc(&mvm->pending_frames[mvmsta->sta_id]); return 0; drop_unlock_sta: iwl_trans_free_tx_cmd(mvm->trans, dev_cmd); spin_unlock(&mvmsta->lock); drop: return -1; }
static inline int iwl_mvm_check_pn(struct iwl_mvm *mvm, struct sk_buff *skb, int queue, struct ieee80211_sta *sta) { struct iwl_mvm_sta *mvmsta; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct ieee80211_rx_status *stats = IEEE80211_SKB_RXCB(skb); struct iwl_mvm_key_pn *ptk_pn; u8 tid, keyidx; u8 pn[IEEE80211_CCMP_PN_LEN]; u8 *extiv; /* do PN checking */ /* multicast and non-data only arrives on default queue */ if (!ieee80211_is_data(hdr->frame_control) || is_multicast_ether_addr(hdr->addr1)) return 0; /* do not check PN for open AP */ if (!(stats->flag & RX_FLAG_DECRYPTED)) return 0; /* * avoid checking for default queue - we don't want to replicate * all the logic that's necessary for checking the PN on fragmented * frames, leave that to mac80211 */ if (queue == 0) return 0; /* if we are here - this for sure is either CCMP or GCMP */ if (IS_ERR_OR_NULL(sta)) { IWL_ERR(mvm, "expected hw-decrypted unicast frame for station\n"); return -1; } mvmsta = iwl_mvm_sta_from_mac80211(sta); extiv = (u8 *)hdr + ieee80211_hdrlen(hdr->frame_control); keyidx = extiv[3] >> 6; ptk_pn = rcu_dereference(mvmsta->ptk_pn[keyidx]); if (!ptk_pn) return -1; if (ieee80211_is_data_qos(hdr->frame_control)) tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK; else tid = 0; /* we don't use HCCA/802.11 QoS TSPECs, so drop such frames */ if (tid >= IWL_MAX_TID_COUNT) return -1; /* load pn */ pn[0] = extiv[7]; pn[1] = extiv[6]; pn[2] = extiv[5]; pn[3] = extiv[4]; pn[4] = extiv[1]; pn[5] = extiv[0]; if (memcmp(pn, ptk_pn->q[queue].pn[tid], IEEE80211_CCMP_PN_LEN) <= 0) return -1; memcpy(ptk_pn->q[queue].pn[tid], pn, IEEE80211_CCMP_PN_LEN); stats->flag |= RX_FLAG_PN_VALIDATED; return 0; }
void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi, struct iwl_rx_cmd_buffer *rxb, int queue) { struct ieee80211_rx_status *rx_status; struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_rx_mpdu_desc *desc = (void *)pkt->data; struct ieee80211_hdr *hdr = (void *)(desc + 1); u32 len = le16_to_cpu(desc->mpdu_len); u32 rate_n_flags = le32_to_cpu(desc->rate_n_flags); struct ieee80211_sta *sta = NULL; struct sk_buff *skb; u8 crypt_len = 0; /* Dont use dev_alloc_skb(), we'll have enough headroom once * ieee80211_hdr pulled. */ skb = alloc_skb(128, GFP_ATOMIC); if (!skb) { IWL_ERR(mvm, "alloc_skb failed\n"); return; } rx_status = IEEE80211_SKB_RXCB(skb); if (iwl_mvm_rx_crypto(mvm, hdr, rx_status, desc, queue, &crypt_len)) { kfree_skb(skb); return; } /* * Keep packets with CRC errors (and with overrun) for monitor mode * (otherwise the firmware discards them) but mark them as bad. */ if (!(desc->status & cpu_to_le16(IWL_RX_MPDU_STATUS_CRC_OK)) || !(desc->status & cpu_to_le16(IWL_RX_MPDU_STATUS_OVERRUN_OK))) { IWL_DEBUG_RX(mvm, "Bad CRC or FIFO: 0x%08X.\n", le16_to_cpu(desc->status)); rx_status->flag |= RX_FLAG_FAILED_FCS_CRC; } rx_status->mactime = le64_to_cpu(desc->tsf_on_air_rise); rx_status->device_timestamp = le32_to_cpu(desc->gp2_on_air_rise); rx_status->band = desc->channel > 14 ? IEEE80211_BAND_5GHZ : IEEE80211_BAND_2GHZ; rx_status->freq = ieee80211_channel_to_frequency(desc->channel, rx_status->band); iwl_mvm_get_signal_strength(mvm, desc, rx_status); rcu_read_lock(); if (le16_to_cpu(desc->status) & IWL_RX_MPDU_STATUS_SRC_STA_FOUND) { u8 id = desc->sta_id_flags & IWL_RX_MPDU_SIF_STA_ID_MASK; if (!WARN_ON_ONCE(id >= IWL_MVM_STATION_COUNT)) { sta = rcu_dereference(mvm->fw_id_to_mac_id[id]); if (IS_ERR(sta)) sta = NULL; } } else if (!is_multicast_ether_addr(hdr->addr2)) { /* * This is fine since we prevent two stations with the same * address from being added. */ sta = ieee80211_find_sta_by_ifaddr(mvm->hw, hdr->addr2, NULL); } if (sta) { struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); /* * We have tx blocked stations (with CS bit). If we heard * frames from a blocked station on a new channel we can * TX to it again. */ if (unlikely(mvm->csa_tx_block_bcn_timeout)) iwl_mvm_sta_modify_disable_tx_ap(mvm, sta, false); rs_update_last_rssi(mvm, &mvmsta->lq_sta, rx_status); if (iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_RSSI) && ieee80211_is_beacon(hdr->frame_control)) { struct iwl_fw_dbg_trigger_tlv *trig; struct iwl_fw_dbg_trigger_low_rssi *rssi_trig; bool trig_check; s32 rssi; trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_RSSI); rssi_trig = (void *)trig->data; rssi = le32_to_cpu(rssi_trig->rssi); trig_check = iwl_fw_dbg_trigger_check_stop(mvm, mvmsta->vif, trig); if (trig_check && rx_status->signal < rssi) iwl_mvm_fw_dbg_collect_trig(mvm, trig, NULL); } /* TODO: multi queue TCM */ if (ieee80211_is_data(hdr->frame_control)) iwl_mvm_rx_csum(sta, skb, desc); } /* * TODO: PHY info. * Verify we don't have the information in the MPDU descriptor and * that it is not needed. * Make sure for monitor mode that we are on default queue, update * ampdu_ref and the rest of phy info then */ /* Set up the HT phy flags */ switch (rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK) { case RATE_MCS_CHAN_WIDTH_20: break; case RATE_MCS_CHAN_WIDTH_40: rx_status->flag |= RX_FLAG_40MHZ; break; case RATE_MCS_CHAN_WIDTH_80: rx_status->vht_flag |= RX_VHT_FLAG_80MHZ; break; case RATE_MCS_CHAN_WIDTH_160: rx_status->vht_flag |= RX_VHT_FLAG_160MHZ; break; } if (rate_n_flags & RATE_MCS_SGI_MSK) rx_status->flag |= RX_FLAG_SHORT_GI; if (rate_n_flags & RATE_HT_MCS_GF_MSK) rx_status->flag |= RX_FLAG_HT_GF; if (rate_n_flags & RATE_MCS_LDPC_MSK) rx_status->flag |= RX_FLAG_LDPC; if (rate_n_flags & RATE_MCS_HT_MSK) { u8 stbc = (rate_n_flags & RATE_MCS_HT_STBC_MSK) >> RATE_MCS_STBC_POS; rx_status->flag |= RX_FLAG_HT; rx_status->rate_idx = rate_n_flags & RATE_HT_MCS_INDEX_MSK; rx_status->flag |= stbc << RX_FLAG_STBC_SHIFT; } else if (rate_n_flags & RATE_MCS_VHT_MSK) {
void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi, struct iwl_rx_cmd_buffer *rxb, int queue) { struct ieee80211_rx_status *rx_status; struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_rx_mpdu_desc *desc = (void *)pkt->data; struct ieee80211_hdr *hdr = (void *)(pkt->data + sizeof(*desc)); u32 len = le16_to_cpu(desc->mpdu_len); u32 rate_n_flags = le32_to_cpu(desc->rate_n_flags); u16 phy_info = le16_to_cpu(desc->phy_info); struct ieee80211_sta *sta = NULL; struct sk_buff *skb; u8 crypt_len = 0; /* Dont use dev_alloc_skb(), we'll have enough headroom once * ieee80211_hdr pulled. */ skb = alloc_skb(128, GFP_ATOMIC); if (!skb) { IWL_ERR(mvm, "alloc_skb failed\n"); return; } rx_status = IEEE80211_SKB_RXCB(skb); if (iwl_mvm_rx_crypto(mvm, hdr, rx_status, desc, queue, &crypt_len)) { kfree_skb(skb); return; } /* * Keep packets with CRC errors (and with overrun) for monitor mode * (otherwise the firmware discards them) but mark them as bad. */ if (!(desc->status & cpu_to_le16(IWL_RX_MPDU_STATUS_CRC_OK)) || !(desc->status & cpu_to_le16(IWL_RX_MPDU_STATUS_OVERRUN_OK))) { IWL_DEBUG_RX(mvm, "Bad CRC or FIFO: 0x%08X.\n", le16_to_cpu(desc->status)); rx_status->flag |= RX_FLAG_FAILED_FCS_CRC; } /* set the preamble flag if appropriate */ if (phy_info & IWL_RX_MPDU_PHY_SHORT_PREAMBLE) rx_status->flag |= RX_FLAG_SHORTPRE; if (likely(!(phy_info & IWL_RX_MPDU_PHY_TSF_OVERLOAD))) { rx_status->mactime = le64_to_cpu(desc->tsf_on_air_rise); /* TSF as indicated by the firmware is at INA time */ rx_status->flag |= RX_FLAG_MACTIME_PLCP_START; } rx_status->device_timestamp = le32_to_cpu(desc->gp2_on_air_rise); rx_status->band = desc->channel > 14 ? NL80211_BAND_5GHZ : NL80211_BAND_2GHZ; rx_status->freq = ieee80211_channel_to_frequency(desc->channel, rx_status->band); iwl_mvm_get_signal_strength(mvm, desc, rx_status); /* update aggregation data for monitor sake on default queue */ if (!queue && (phy_info & IWL_RX_MPDU_PHY_AMPDU)) { bool toggle_bit = phy_info & IWL_RX_MPDU_PHY_AMPDU_TOGGLE; rx_status->flag |= RX_FLAG_AMPDU_DETAILS; rx_status->ampdu_reference = mvm->ampdu_ref; /* toggle is switched whenever new aggregation starts */ if (toggle_bit != mvm->ampdu_toggle) { mvm->ampdu_ref++; mvm->ampdu_toggle = toggle_bit; } } rcu_read_lock(); if (le16_to_cpu(desc->status) & IWL_RX_MPDU_STATUS_SRC_STA_FOUND) { u8 id = desc->sta_id_flags & IWL_RX_MPDU_SIF_STA_ID_MASK; if (!WARN_ON_ONCE(id >= IWL_MVM_STATION_COUNT)) { sta = rcu_dereference(mvm->fw_id_to_mac_id[id]); if (IS_ERR(sta)) sta = NULL; } } else if (!is_multicast_ether_addr(hdr->addr2)) { /* * This is fine since we prevent two stations with the same * address from being added. */ sta = ieee80211_find_sta_by_ifaddr(mvm->hw, hdr->addr2, NULL); } if (sta) { struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); struct ieee80211_vif *tx_blocked_vif = rcu_dereference(mvm->csa_tx_blocked_vif); u8 baid = (u8)((le32_to_cpu(desc->reorder_data) & IWL_RX_MPDU_REORDER_BAID_MASK) >> IWL_RX_MPDU_REORDER_BAID_SHIFT); /* * We have tx blocked stations (with CS bit). If we heard * frames from a blocked station on a new channel we can * TX to it again. */ if (unlikely(tx_blocked_vif) && tx_blocked_vif == mvmsta->vif) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(tx_blocked_vif); if (mvmvif->csa_target_freq == rx_status->freq) iwl_mvm_sta_modify_disable_tx_ap(mvm, sta, false); } rs_update_last_rssi(mvm, &mvmsta->lq_sta, rx_status); if (iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_RSSI) && ieee80211_is_beacon(hdr->frame_control)) { struct iwl_fw_dbg_trigger_tlv *trig; struct iwl_fw_dbg_trigger_low_rssi *rssi_trig; bool trig_check; s32 rssi; trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_RSSI); rssi_trig = (void *)trig->data; rssi = le32_to_cpu(rssi_trig->rssi); trig_check = iwl_fw_dbg_trigger_check_stop(mvm, mvmsta->vif, trig); if (trig_check && rx_status->signal < rssi) iwl_mvm_fw_dbg_collect_trig(mvm, trig, NULL); } if (ieee80211_is_data(hdr->frame_control)) iwl_mvm_rx_csum(sta, skb, desc); if (iwl_mvm_is_nonagg_dup(sta, queue, rx_status, hdr, desc)) { kfree_skb(skb); rcu_read_unlock(); return; } /* * Our hardware de-aggregates AMSDUs but copies the mac header * as it to the de-aggregated MPDUs. We need to turn off the * AMSDU bit in the QoS control ourselves. */ if ((desc->mac_flags2 & IWL_RX_MPDU_MFLG2_AMSDU) && !WARN_ON(!ieee80211_is_data_qos(hdr->frame_control))) { u8 *qc = ieee80211_get_qos_ctl(hdr); *qc &= ~IEEE80211_QOS_CTL_A_MSDU_PRESENT; } if (baid != IWL_RX_REORDER_DATA_INVALID_BAID) iwl_mvm_agg_rx_received(mvm, baid); } /* Set up the HT phy flags */ switch (rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK) { case RATE_MCS_CHAN_WIDTH_20: break; case RATE_MCS_CHAN_WIDTH_40: rx_status->flag |= RX_FLAG_40MHZ; break; case RATE_MCS_CHAN_WIDTH_80: rx_status->vht_flag |= RX_VHT_FLAG_80MHZ; break; case RATE_MCS_CHAN_WIDTH_160: rx_status->vht_flag |= RX_VHT_FLAG_160MHZ; break; } if (rate_n_flags & RATE_MCS_SGI_MSK) rx_status->flag |= RX_FLAG_SHORT_GI; if (rate_n_flags & RATE_HT_MCS_GF_MSK) rx_status->flag |= RX_FLAG_HT_GF; if (rate_n_flags & RATE_MCS_LDPC_MSK) rx_status->flag |= RX_FLAG_LDPC; if (rate_n_flags & RATE_MCS_HT_MSK) { u8 stbc = (rate_n_flags & RATE_MCS_HT_STBC_MSK) >> RATE_MCS_STBC_POS; rx_status->flag |= RX_FLAG_HT; rx_status->rate_idx = rate_n_flags & RATE_HT_MCS_INDEX_MSK; rx_status->flag |= stbc << RX_FLAG_STBC_SHIFT; } else if (rate_n_flags & RATE_MCS_VHT_MSK) {
/* send station add/update command to firmware */ int iwl_mvm_sta_send_to_fw(struct iwl_mvm *mvm, struct ieee80211_sta *sta, bool update) { struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); struct iwl_mvm_add_sta_cmd add_sta_cmd = { .sta_id = mvm_sta->sta_id, .mac_id_n_color = cpu_to_le32(mvm_sta->mac_id_n_color), .add_modify = update ? 1 : 0, .station_flags_msk = cpu_to_le32(STA_FLG_FAT_EN_MSK | STA_FLG_MIMO_EN_MSK), }; int ret; u32 status; u32 agg_size = 0, mpdu_dens = 0; if (!update) { add_sta_cmd.tfd_queue_msk = cpu_to_le32(mvm_sta->tfd_queue_msk); memcpy(&add_sta_cmd.addr, sta->addr, ETH_ALEN); } switch (sta->bandwidth) { case IEEE80211_STA_RX_BW_160: add_sta_cmd.station_flags |= cpu_to_le32(STA_FLG_FAT_EN_160MHZ); /* fall through */ case IEEE80211_STA_RX_BW_80: add_sta_cmd.station_flags |= cpu_to_le32(STA_FLG_FAT_EN_80MHZ); /* fall through */ case IEEE80211_STA_RX_BW_40: add_sta_cmd.station_flags |= cpu_to_le32(STA_FLG_FAT_EN_40MHZ); /* fall through */ case IEEE80211_STA_RX_BW_20: if (sta->ht_cap.ht_supported) add_sta_cmd.station_flags |= cpu_to_le32(STA_FLG_FAT_EN_20MHZ); break; } switch (sta->rx_nss) { case 1: add_sta_cmd.station_flags |= cpu_to_le32(STA_FLG_MIMO_EN_SISO); break; case 2: add_sta_cmd.station_flags |= cpu_to_le32(STA_FLG_MIMO_EN_MIMO2); break; case 3 ... 8: add_sta_cmd.station_flags |= cpu_to_le32(STA_FLG_MIMO_EN_MIMO3); break; } switch (sta->smps_mode) { case IEEE80211_SMPS_AUTOMATIC: case IEEE80211_SMPS_NUM_MODES: WARN_ON(1); break; case IEEE80211_SMPS_STATIC: /* override NSS */ add_sta_cmd.station_flags &= ~cpu_to_le32(STA_FLG_MIMO_EN_MSK); add_sta_cmd.station_flags |= cpu_to_le32(STA_FLG_MIMO_EN_SISO); break; case IEEE80211_SMPS_DYNAMIC: add_sta_cmd.station_flags |= cpu_to_le32(STA_FLG_RTS_MIMO_PROT); break; case IEEE80211_SMPS_OFF: /* nothing */ break; } if (sta->ht_cap.ht_supported) { add_sta_cmd.station_flags_msk |= cpu_to_le32(STA_FLG_MAX_AGG_SIZE_MSK | STA_FLG_AGG_MPDU_DENS_MSK); mpdu_dens = sta->ht_cap.ampdu_density; } if (sta->vht_cap.vht_supported) { agg_size = sta->vht_cap.cap & IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK; agg_size >>= IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT; } else if (sta->ht_cap.ht_supported) {
/* * Returns true if the MPDU was buffered\dropped, false if it should be passed * to upper layer. */ static bool iwl_mvm_reorder(struct iwl_mvm *mvm, struct napi_struct *napi, int queue, struct ieee80211_sta *sta, struct sk_buff *skb, struct iwl_rx_mpdu_desc *desc) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); struct iwl_mvm_baid_data *baid_data; struct iwl_mvm_reorder_buffer *buffer; struct sk_buff *tail; u32 reorder = le32_to_cpu(desc->reorder_data); bool amsdu = desc->mac_flags2 & IWL_RX_MPDU_MFLG2_AMSDU; u8 tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK; u8 sub_frame_idx = desc->amsdu_info & IWL_RX_MPDU_AMSDU_SUBFRAME_IDX_MASK; int index; u16 nssn, sn; u8 baid; baid = (reorder & IWL_RX_MPDU_REORDER_BAID_MASK) >> IWL_RX_MPDU_REORDER_BAID_SHIFT; if (baid == IWL_RX_REORDER_DATA_INVALID_BAID) return false; /* no sta yet */ if (WARN_ON(IS_ERR_OR_NULL(sta))) return false; /* not a data packet */ if (!ieee80211_is_data_qos(hdr->frame_control) || is_multicast_ether_addr(hdr->addr1)) return false; if (unlikely(!ieee80211_is_data_present(hdr->frame_control))) return false; baid_data = rcu_dereference(mvm->baid_map[baid]); if (WARN(!baid_data, "Received baid %d, but no data exists for this BAID\n", baid)) return false; if (WARN(tid != baid_data->tid || mvm_sta->sta_id != baid_data->sta_id, "baid 0x%x is mapped to sta:%d tid:%d, but was received for sta:%d tid:%d\n", baid, baid_data->sta_id, baid_data->tid, mvm_sta->sta_id, tid)) return false; nssn = reorder & IWL_RX_MPDU_REORDER_NSSN_MASK; sn = (reorder & IWL_RX_MPDU_REORDER_SN_MASK) >> IWL_RX_MPDU_REORDER_SN_SHIFT; buffer = &baid_data->reorder_buf[queue]; spin_lock_bh(&buffer->lock); /* * If there was a significant jump in the nssn - adjust. * If the SN is smaller than the NSSN it might need to first go into * the reorder buffer, in which case we just release up to it and the * rest of the function will take of storing it and releasing up to the * nssn */ if (!iwl_mvm_is_sn_less(nssn, buffer->head_sn + buffer->buf_size, buffer->buf_size)) { u16 min_sn = ieee80211_sn_less(sn, nssn) ? sn : nssn; iwl_mvm_release_frames(mvm, sta, napi, buffer, min_sn); } /* drop any oudated packets */ if (ieee80211_sn_less(sn, buffer->head_sn)) goto drop; /* release immediately if allowed by nssn and no stored frames */ if (!buffer->num_stored && ieee80211_sn_less(sn, nssn)) { if (iwl_mvm_is_sn_less(buffer->head_sn, nssn, buffer->buf_size)) buffer->head_sn = nssn; /* No need to update AMSDU last SN - we are moving the head */ spin_unlock_bh(&buffer->lock); return false; } index = sn % buffer->buf_size; /* * Check if we already stored this frame * As AMSDU is either received or not as whole, logic is simple: * If we have frames in that position in the buffer and the last frame * originated from AMSDU had a different SN then it is a retransmission. * If it is the same SN then if the subframe index is incrementing it * is the same AMSDU - otherwise it is a retransmission. */ tail = skb_peek_tail(&buffer->entries[index]); if (tail && !amsdu) goto drop; else if (tail && (sn != buffer->last_amsdu || buffer->last_sub_index >= sub_frame_idx)) goto drop; /* put in reorder buffer */ __skb_queue_tail(&buffer->entries[index], skb); buffer->num_stored++; buffer->reorder_time[index] = jiffies; if (amsdu) { buffer->last_amsdu = sn; buffer->last_sub_index = sub_frame_idx; } iwl_mvm_release_frames(mvm, sta, napi, buffer, nssn); spin_unlock_bh(&buffer->lock); return true; drop: kfree_skb(skb); spin_unlock_bh(&buffer->lock); return true; }