diff options
Diffstat (limited to 'drivers/net/wireless/iwlwifi/iwl-agn-tx.c')
| -rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-agn-tx.c | 89 | 
1 files changed, 61 insertions, 28 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c index 7d614c4d3c6..55a1b31fd09 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c @@ -233,6 +233,7 @@ int iwlagn_txq_agg_enable(struct iwl_priv *priv, int txq_id,  {  	unsigned long flags;  	u16 ra_tid; +	int ret;  	if ((IWLAGN_FIRST_AMPDU_QUEUE > txq_id) ||  	    (IWLAGN_FIRST_AMPDU_QUEUE + priv->cfg->num_of_ampdu_queues @@ -248,7 +249,9 @@ int iwlagn_txq_agg_enable(struct iwl_priv *priv, int txq_id,  	ra_tid = BUILD_RAxTID(sta_id, tid);  	/* Modify device's station table to Tx this TID */ -	iwl_sta_tx_modify_enable_tid(priv, sta_id, tid); +	ret = iwl_sta_tx_modify_enable_tid(priv, sta_id, tid); +	if (ret) +		return ret;  	spin_lock_irqsave(&priv->lock, flags); @@ -469,7 +472,8 @@ static void iwlagn_tx_cmd_build_rate(struct iwl_priv *priv,  	}  	/* Set up antennas */ -	priv->mgmt_tx_ant = iwl_toggle_tx_ant(priv, priv->mgmt_tx_ant); +	priv->mgmt_tx_ant = iwl_toggle_tx_ant(priv, priv->mgmt_tx_ant, +					      priv->hw_params.valid_tx_ant);  	rate_flags |= iwl_ant_idx_to_flags(priv->mgmt_tx_ant);  	/* Set the rate in the TX cmd */ @@ -567,10 +571,7 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)  	hdr_len = ieee80211_hdrlen(fc);  	/* Find index into station table for destination station */ -	if (!info->control.sta) -		sta_id = priv->hw_params.bcast_sta_id; -	else -		sta_id = iwl_sta_id(info->control.sta); +	sta_id = iwl_sta_id_or_broadcast(priv, info->control.sta);  	if (sta_id == IWL_INVALID_STATION) {  		IWL_DEBUG_DROP(priv, "Dropping - INVALID STATION: %pM\n",  			       hdr->addr1); @@ -598,11 +599,17 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)  	}  	txq_id = get_queue_from_ac(skb_get_queue_mapping(skb)); + +	/* irqs already disabled/saved above when locking priv->lock */ +	spin_lock(&priv->sta_lock); +  	if (ieee80211_is_data_qos(fc)) {  		qc = ieee80211_get_qos_ctl(hdr);  		tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK; -		if (unlikely(tid >= MAX_TID_COUNT)) +		if (WARN_ON_ONCE(tid >= MAX_TID_COUNT)) { +			spin_unlock(&priv->sta_lock);  			goto drop_unlock; +		}  		seq_number = priv->stations[sta_id].tid[tid].seq_number;  		seq_number &= IEEE80211_SCTL_SEQ;  		hdr->seq_ctrl = hdr->seq_ctrl & @@ -620,15 +627,22 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)  	swq_id = txq->swq_id;  	q = &txq->q; -	if (unlikely(iwl_queue_space(q) < q->high_mark)) +	if (unlikely(iwl_queue_space(q) < q->high_mark)) { +		spin_unlock(&priv->sta_lock);  		goto drop_unlock; +	} -	if (ieee80211_is_data_qos(fc)) +	if (ieee80211_is_data_qos(fc)) {  		priv->stations[sta_id].tid[tid].tfds_in_queue++; +		if (!ieee80211_has_morefrags(fc)) +			priv->stations[sta_id].tid[tid].seq_number = seq_number; +	} + +	spin_unlock(&priv->sta_lock);  	/* Set up driver data for this TFD */  	memset(&(txq->txb[q->write_ptr]), 0, sizeof(struct iwl_tx_info)); -	txq->txb[q->write_ptr].skb[0] = skb; +	txq->txb[q->write_ptr].skb = skb;  	/* Set up first empty entry in queue's array of Tx/cmd buffers */  	out_cmd = txq->cmd[q->write_ptr]; @@ -694,8 +708,8 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)  	txcmd_phys = pci_map_single(priv->pci_dev,  				    &out_cmd->hdr, len,  				    PCI_DMA_BIDIRECTIONAL); -	pci_unmap_addr_set(out_meta, mapping, txcmd_phys); -	pci_unmap_len_set(out_meta, len, len); +	dma_unmap_addr_set(out_meta, mapping, txcmd_phys); +	dma_unmap_len_set(out_meta, len, len);  	/* Add buffer containing Tx command and MAC(!) header to TFD's  	 * first entry */  	priv->cfg->ops->lib->txq_attach_buf_to_tfd(priv, txq, @@ -703,8 +717,6 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)  	if (!ieee80211_has_morefrags(hdr->frame_control)) {  		txq->need_update = 1; -		if (qc) -			priv->stations[sta_id].tid[tid].seq_number = seq_number;  	} else {  		wait_write_ptr = 1;  		txq->need_update = 0; @@ -938,9 +950,12 @@ void iwlagn_txq_ctx_stop(struct iwl_priv *priv)  	/* Stop each Tx DMA channel, and wait for it to be idle */  	for (ch = 0; ch < priv->hw_params.dma_chnl_num; ch++) {  		iwl_write_direct32(priv, FH_TCSR_CHNL_TX_CONFIG_REG(ch), 0x0); -		iwl_poll_direct_bit(priv, FH_TSSR_TX_STATUS_REG, +		if (iwl_poll_direct_bit(priv, FH_TSSR_TX_STATUS_REG,  				    FH_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(ch), -				    1000); +				    1000)) +			IWL_ERR(priv, "Failing on timeout while stopping" +			    " DMA channel %d [0x%08x]", ch, +			    iwl_read_direct32(priv, FH_TSSR_TX_STATUS_REG));  	}  	spin_unlock_irqrestore(&priv->lock, flags);  } @@ -1009,6 +1024,8 @@ int iwlagn_tx_agg_start(struct iwl_priv *priv, struct ieee80211_vif *vif,  	if (ret)  		return ret; +	spin_lock_irqsave(&priv->sta_lock, flags); +	tid_data = &priv->stations[sta_id].tid[tid];  	if (tid_data->tfds_in_queue == 0) {  		IWL_DEBUG_HT(priv, "HW queue is empty\n");  		tid_data->agg.state = IWL_AGG_ON; @@ -1018,6 +1035,7 @@ int iwlagn_tx_agg_start(struct iwl_priv *priv, struct ieee80211_vif *vif,  			     tid_data->tfds_in_queue);  		tid_data->agg.state = IWL_EMPTYING_HW_QUEUE_ADDBA;  	} +	spin_unlock_irqrestore(&priv->sta_lock, flags);  	return ret;  } @@ -1040,11 +1058,14 @@ int iwlagn_tx_agg_stop(struct iwl_priv *priv, struct ieee80211_vif *vif,  		return -ENXIO;  	} +	spin_lock_irqsave(&priv->sta_lock, flags); +  	if (priv->stations[sta_id].tid[tid].agg.state ==  				IWL_EMPTYING_HW_QUEUE_ADDBA) {  		IWL_DEBUG_HT(priv, "AGG stop before setup done\n");  		ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);  		priv->stations[sta_id].tid[tid].agg.state = IWL_AGG_OFF; +		spin_unlock_irqrestore(&priv->sta_lock, flags);  		return 0;  	} @@ -1062,13 +1083,17 @@ int iwlagn_tx_agg_stop(struct iwl_priv *priv, struct ieee80211_vif *vif,  		IWL_DEBUG_HT(priv, "Stopping a non empty AGG HW QUEUE\n");  		priv->stations[sta_id].tid[tid].agg.state =  				IWL_EMPTYING_HW_QUEUE_DELBA; +		spin_unlock_irqrestore(&priv->sta_lock, flags);  		return 0;  	}  	IWL_DEBUG_HT(priv, "HW queue is empty\n");  	priv->stations[sta_id].tid[tid].agg.state = IWL_AGG_OFF; -	spin_lock_irqsave(&priv->lock, flags); +	/* do not restore/save irqs */ +	spin_unlock(&priv->sta_lock); +	spin_lock(&priv->lock); +  	/*  	 * the only reason this call can fail is queue number out of range,  	 * which can happen if uCode is reloaded and all the station @@ -1092,6 +1117,8 @@ int iwlagn_txq_check_empty(struct iwl_priv *priv,  	u8 *addr = priv->stations[sta_id].sta.sta.addr;  	struct iwl_tid_data *tid_data = &priv->stations[sta_id].tid[tid]; +	WARN_ON(!spin_is_locked(&priv->sta_lock)); +  	switch (priv->stations[sta_id].tid[tid].agg.state) {  	case IWL_EMPTYING_HW_QUEUE_DELBA:  		/* We are reclaiming the last packet of the */ @@ -1116,6 +1143,7 @@ int iwlagn_txq_check_empty(struct iwl_priv *priv,  		}  		break;  	} +  	return 0;  } @@ -1159,12 +1187,12 @@ int iwlagn_tx_queue_reclaim(struct iwl_priv *priv, int txq_id, int index)  	     q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd)) {  		tx_info = &txq->txb[txq->q.read_ptr]; -		iwlagn_tx_status(priv, tx_info->skb[0]); +		iwlagn_tx_status(priv, tx_info->skb); -		hdr = (struct ieee80211_hdr *)tx_info->skb[0]->data; +		hdr = (struct ieee80211_hdr *)tx_info->skb->data;  		if (hdr && ieee80211_is_data_qos(hdr->frame_control))  			nfreed++; -		tx_info->skb[0] = NULL; +		tx_info->skb = NULL;  		if (priv->cfg->ops->lib->txq_inval_byte_cnt_tbl)  			priv->cfg->ops->lib->txq_inval_byte_cnt_tbl(priv, txq); @@ -1188,7 +1216,7 @@ static int iwlagn_tx_status_reply_compressed_ba(struct iwl_priv *priv,  	int i, sh, ack;  	u16 seq_ctl = le16_to_cpu(ba_resp->seq_ctl);  	u16 scd_flow = le16_to_cpu(ba_resp->scd_flow); -	u64 bitmap; +	u64 bitmap, sent_bitmap;  	int successes = 0;  	struct ieee80211_tx_info *info; @@ -1216,24 +1244,26 @@ static int iwlagn_tx_status_reply_compressed_ba(struct iwl_priv *priv,  	/* check for success or failure according to the  	 * transmitted bitmap and block-ack bitmap */ -	bitmap &= agg->bitmap; +	sent_bitmap = bitmap & agg->bitmap;  	/* For each frame attempted in aggregation,  	 * update driver's record of tx frame's status. */ -	for (i = 0; i < agg->frame_count ; i++) { -		ack = bitmap & (1ULL << i); -		successes += !!ack; +	i = 0; +	while (sent_bitmap) { +		ack = sent_bitmap & 1ULL; +		successes += ack;  		IWL_DEBUG_TX_REPLY(priv, "%s ON i=%d idx=%d raw=%d\n",  			ack ? "ACK" : "NACK", i, (agg->start_idx + i) & 0xff,  			agg->start_idx + i); +		sent_bitmap >>= 1; +		++i;  	} -	info = IEEE80211_SKB_CB(priv->txq[scd_flow].txb[agg->start_idx].skb[0]); +	info = IEEE80211_SKB_CB(priv->txq[scd_flow].txb[agg->start_idx].skb);  	memset(&info->status, 0, sizeof(info->status));  	info->flags |= IEEE80211_TX_STAT_ACK;  	info->flags |= IEEE80211_TX_STAT_AMPDU;  	info->status.ampdu_ack_len = successes; -	info->status.ampdu_ack_map = bitmap;  	info->status.ampdu_len = agg->frame_count;  	iwlagn_hwrate_to_tx_control(priv, agg->rate_n_flags, info); @@ -1281,6 +1311,7 @@ void iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv,  	int index;  	int sta_id;  	int tid; +	unsigned long flags;  	/* "flow" corresponds to Tx queue */  	u16 scd_flow = le16_to_cpu(ba_resp->scd_flow); @@ -1308,7 +1339,7 @@ void iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv,  	/* Find index just before block-ack window */  	index = iwl_queue_dec_wrap(ba_resp_scd_ssn & 0xff, txq->q.n_bd); -	/* TODO: Need to get this copy more safely - now good for debug */ +	spin_lock_irqsave(&priv->sta_lock, flags);  	IWL_DEBUG_TX_REPLY(priv, "REPLY_COMPRESSED_BA [%d] Received from %pM, "  			   "sta_id = %d\n", @@ -1344,4 +1375,6 @@ void iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv,  		iwlagn_txq_check_empty(priv, sta_id, tid, scd_flow);  	} + +	spin_unlock_irqrestore(&priv->sta_lock, flags);  }  |