int iwl_mvm_tx_skb_non_sta(struct iwl_mvm *mvm, struct sk_buff *skb) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct iwl_device_cmd *dev_cmd; struct iwl_tx_cmd *tx_cmd; u8 sta_id; if (WARN_ON_ONCE(info->flags & IEEE80211_TX_CTL_AMPDU)) return -1; if (WARN_ON_ONCE(info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM && (!info->control.vif || info->hw_queue != info->control.vif->cab_queue))) return -1; /* * IWL_MVM_OFFCHANNEL_QUEUE is used for ROC packets that can be used * in 2 different types of vifs, P2P & STATION. P2P uses the offchannel * queue. STATION (HS2.0) uses the auxiliary context of the FW, * and hence needs to be sent on the aux queue */ if (IEEE80211_SKB_CB(skb)->hw_queue == IWL_MVM_OFFCHANNEL_QUEUE && info->control.vif->type == NL80211_IFTYPE_STATION) IEEE80211_SKB_CB(skb)->hw_queue = mvm->aux_queue; /* * If the interface on which frame is sent is the P2P_DEVICE * or an AP/GO interface use the broadcast station associated * with it; otherwise use the AUX station. */ if (info->control.vif && (info->control.vif->type == NL80211_IFTYPE_P2P_DEVICE || info->control.vif->type == NL80211_IFTYPE_AP)) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(info->control.vif); sta_id = mvmvif->bcast_sta.sta_id; } else { sta_id = mvm->aux_sta.sta_id; } IWL_DEBUG_TX(mvm, "station Id %d, queue=%d\n", sta_id, info->hw_queue); dev_cmd = iwl_mvm_set_tx_params(mvm, skb, NULL, sta_id); if (!dev_cmd) return -1; /* From now on, we cannot access info->control */ tx_cmd = (struct iwl_tx_cmd *)dev_cmd->payload; /* Copy MAC header from skb into command buffer */ memcpy(tx_cmd->hdr, hdr, ieee80211_hdrlen(hdr->frame_control)); if (iwl_trans_tx(mvm->trans, skb, dev_cmd, info->hw_queue)) { iwl_trans_free_tx_cmd(mvm->trans, dev_cmd); return -1; } return 0; }
static void iwlagn_tx_cmd_build_hwcrypto(struct iwl_priv *priv, struct ieee80211_tx_info *info, struct iwl_tx_cmd *tx_cmd, struct sk_buff *skb_frag, int sta_id) { struct ieee80211_key_conf *keyconf = info->control.hw_key; switch (keyconf->cipher) { case WLAN_CIPHER_SUITE_CCMP: tx_cmd->sec_ctl = TX_CMD_SEC_CCM; memcpy(tx_cmd->key, keyconf->key, keyconf->keylen); if (info->flags & IEEE80211_TX_CTL_AMPDU) tx_cmd->tx_flags |= TX_CMD_FLG_AGG_CCMP_MSK; IWL_DEBUG_TX(priv, "tx_cmd with AES hwcrypto\n"); break; case WLAN_CIPHER_SUITE_TKIP: tx_cmd->sec_ctl = TX_CMD_SEC_TKIP; ieee80211_get_tkip_p2k(keyconf, skb_frag, tx_cmd->key); IWL_DEBUG_TX(priv, "tx_cmd with tkip hwcrypto\n"); break; case WLAN_CIPHER_SUITE_WEP104: tx_cmd->sec_ctl |= TX_CMD_SEC_KEY128; /* fall through */ case WLAN_CIPHER_SUITE_WEP40: tx_cmd->sec_ctl |= (TX_CMD_SEC_WEP | (keyconf->keyidx & TX_CMD_SEC_MSK) << TX_CMD_SEC_SHIFT); memcpy(&tx_cmd->key[3], keyconf->key, keyconf->keylen); IWL_DEBUG_TX(priv, "Configuring packet for WEP encryption " "with key %d\n", keyconf->keyidx); break; default: IWL_ERR(priv, "Unknown encode cipher %x\n", keyconf->cipher); break; } }
/* * iwl_pcie_gen2_txq_inc_wr_ptr - Send new write index to hardware */ void iwl_pcie_gen2_txq_inc_wr_ptr(struct iwl_trans *trans, struct iwl_txq *txq) { lockdep_assert_held(&txq->lock); IWL_DEBUG_TX(trans, "Q:%d WR: 0x%x\n", txq->id, txq->write_ptr); /* * if not in power-save mode, uCode will never sleep when we're * trying to tx (during RFKILL, we're not trying to tx). */ iwl_write32(trans, HBUS_TARG_WRPTR, txq->write_ptr | (txq->id << 16)); }
int iwl_mvm_tx_skb_non_sta(struct iwl_mvm *mvm, struct sk_buff *skb) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct iwl_device_cmd *dev_cmd; struct iwl_tx_cmd *tx_cmd; u8 sta_id; if (WARN_ON_ONCE(info->flags & IEEE80211_TX_CTL_AMPDU)) return -1; if (WARN_ON_ONCE(info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM && (!info->control.vif || info->hw_queue != info->control.vif->cab_queue))) return -1; /* * If the interface on which frame is sent is the P2P_DEVICE * or an AP/GO interface use the broadcast station associated * with it; otherwise use the AUX station. */ if (info->control.vif && (info->control.vif->type == NL80211_IFTYPE_P2P_DEVICE || info->control.vif->type == NL80211_IFTYPE_AP)) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(info->control.vif); sta_id = mvmvif->bcast_sta.sta_id; } else { sta_id = mvm->aux_sta.sta_id; } IWL_DEBUG_TX(mvm, "station Id %d, queue=%d\n", sta_id, info->hw_queue); dev_cmd = iwl_mvm_set_tx_params(mvm, skb, NULL, sta_id); if (!dev_cmd) return -1; /* From now on, we cannot access info->control */ tx_cmd = (struct iwl_tx_cmd *)dev_cmd->payload; /* Copy MAC header from skb into command buffer */ memcpy(tx_cmd->hdr, hdr, ieee80211_hdrlen(hdr->frame_control)); if (iwl_trans_tx(mvm->trans, skb, dev_cmd, info->hw_queue)) { iwl_trans_free_tx_cmd(mvm->trans, dev_cmd); return -1; } return 0; }
/* * 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 = (void *)sta->drv_priv; 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 */ 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); seq_number += 0x10; 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, 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; 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; }
/* * start REPLY_TX command process */ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct iwl_station_priv *sta_priv = NULL; struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; struct iwl_device_cmd *dev_cmd = NULL; struct iwl_tx_cmd *tx_cmd; __le16 fc; u8 hdr_len; u16 len; u8 sta_id; unsigned long flags; bool is_agg = false; if (info->control.vif) ctx = iwl_rxon_ctx_from_vif(info->control.vif); spin_lock_irqsave(&priv->shrd->lock, flags); if (iwl_is_rfkill(priv->shrd)) { IWL_DEBUG_DROP(priv, "Dropping - RF KILL\n"); goto drop_unlock_priv; } fc = hdr->frame_control; #ifdef CONFIG_IWLWIFI_DEBUG if (ieee80211_is_auth(fc)) IWL_DEBUG_TX(priv, "Sending AUTH frame\n"); else if (ieee80211_is_assoc_req(fc)) IWL_DEBUG_TX(priv, "Sending ASSOC frame\n"); else if (ieee80211_is_reassoc_req(fc)) IWL_DEBUG_TX(priv, "Sending REASSOC frame\n"); #endif if (unlikely(ieee80211_is_probe_resp(fc))) { struct iwl_wipan_noa_data *noa_data = rcu_dereference(priv->noa_data); if (noa_data && pskb_expand_head(skb, 0, noa_data->length, GFP_ATOMIC) == 0) { memcpy(skb_put(skb, noa_data->length), noa_data->data, noa_data->length); hdr = (struct ieee80211_hdr *)skb->data; } } hdr_len = ieee80211_hdrlen(fc); /* For management frames use broadcast id to do not break aggregation */ if (!ieee80211_is_data(fc)) sta_id = ctx->bcast_sta_id; else { /* Find index into station table for destination station */ sta_id = iwl_sta_id_or_broadcast(priv, ctx, info->control.sta); if (sta_id == IWL_INVALID_STATION) { IWL_DEBUG_DROP(priv, "Dropping - INVALID STATION: %pM\n", hdr->addr1); goto drop_unlock_priv; } } IWL_DEBUG_TX(priv, "station Id %d\n", sta_id); if (info->control.sta) sta_priv = (void *)info->control.sta->drv_priv; if (sta_priv && sta_priv->asleep && (info->flags & IEEE80211_TX_CTL_POLL_RESPONSE)) { /* * This sends an asynchronous command to the device, * but we can rely on it being processed before the * next frame is processed -- and the next frame to * this station is the one that will consume this * counter. * For now set the counter to just 1 since we do not * support uAPSD yet. */ iwl_sta_modify_sleep_tx_count(priv, sta_id, 1); } if (info->flags & IEEE80211_TX_CTL_AMPDU) is_agg = true; /* irqs already disabled/saved above when locking priv->shrd->lock */ spin_lock(&priv->shrd->sta_lock); dev_cmd = kmem_cache_alloc(priv->tx_cmd_pool, GFP_ATOMIC); if (unlikely(!dev_cmd)) goto drop_unlock_sta; memset(dev_cmd, 0, sizeof(*dev_cmd)); tx_cmd = (struct iwl_tx_cmd *) dev_cmd->payload; /* Total # bytes to be transmitted */ len = (u16)skb->len; tx_cmd->len = cpu_to_le16(len); if (info->control.hw_key) iwlagn_tx_cmd_build_hwcrypto(priv, info, tx_cmd, skb, sta_id); /* TODO need this for burst mode later on */ iwlagn_tx_cmd_build_basic(priv, skb, tx_cmd, info, hdr, sta_id); iwl_dbg_log_tx_data_frame(priv, len, hdr); iwlagn_tx_cmd_build_rate(priv, tx_cmd, info, fc); iwl_update_stats(priv, true, fc, len); memset(&info->status, 0, sizeof(info->status)); info->driver_data[0] = ctx; info->driver_data[1] = dev_cmd; if (iwl_trans_tx(trans(priv), skb, dev_cmd, ctx->ctxid, sta_id)) goto drop_unlock_sta; spin_unlock(&priv->shrd->sta_lock); spin_unlock_irqrestore(&priv->shrd->lock, flags); /* * Avoid atomic ops if it isn't an associated client. * Also, if this is a packet for aggregation, don't * increase the counter because the ucode will stop * aggregation queues when their respective station * goes to sleep. */ if (sta_priv && sta_priv->client && !is_agg) atomic_inc(&sta_priv->pending_frames); return 0; drop_unlock_sta: if (dev_cmd) kmem_cache_free(priv->tx_cmd_pool, dev_cmd); spin_unlock(&priv->shrd->sta_lock); drop_unlock_priv: spin_unlock_irqrestore(&priv->shrd->lock, flags); return -1; }