diff options
Diffstat (limited to 'drivers/net/wireless/rt2x00/rt2x00queue.c')
| -rw-r--r-- | drivers/net/wireless/rt2x00/rt2x00queue.c | 78 | 
1 files changed, 67 insertions, 11 deletions
diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.c b/drivers/net/wireless/rt2x00/rt2x00queue.c index e488b944a03..4d91795dc6a 100644 --- a/drivers/net/wireless/rt2x00/rt2x00queue.c +++ b/drivers/net/wireless/rt2x00/rt2x00queue.c @@ -87,24 +87,35 @@ struct sk_buff *rt2x00queue_alloc_rxskb(struct queue_entry *entry, gfp_t gfp)  	skbdesc->entry = entry;  	if (test_bit(REQUIRE_DMA, &rt2x00dev->cap_flags)) { -		skbdesc->skb_dma = dma_map_single(rt2x00dev->dev, -						  skb->data, -						  skb->len, -						  DMA_FROM_DEVICE); +		dma_addr_t skb_dma; + +		skb_dma = dma_map_single(rt2x00dev->dev, skb->data, skb->len, +					 DMA_FROM_DEVICE); +		if (unlikely(dma_mapping_error(rt2x00dev->dev, skb_dma))) { +			dev_kfree_skb_any(skb); +			return NULL; +		} + +		skbdesc->skb_dma = skb_dma;  		skbdesc->flags |= SKBDESC_DMA_MAPPED_RX;  	}  	return skb;  } -void rt2x00queue_map_txskb(struct queue_entry *entry) +int rt2x00queue_map_txskb(struct queue_entry *entry)  {  	struct device *dev = entry->queue->rt2x00dev->dev;  	struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb);  	skbdesc->skb_dma =  	    dma_map_single(dev, entry->skb->data, entry->skb->len, DMA_TO_DEVICE); + +	if (unlikely(dma_mapping_error(dev, skbdesc->skb_dma))) +		return -ENOMEM; +  	skbdesc->flags |= SKBDESC_DMA_MAPPED_TX; +	return 0;  }  EXPORT_SYMBOL_GPL(rt2x00queue_map_txskb); @@ -343,10 +354,7 @@ static void rt2x00queue_create_tx_descriptor_ht(struct rt2x00_dev *rt2x00dev,  		 * when using more then one tx stream (>MCS7).  		 */  		if (sta && txdesc->u.ht.mcs > 7 && -		    ((sta->ht_cap.cap & -		      IEEE80211_HT_CAP_SM_PS) >> -		     IEEE80211_HT_CAP_SM_PS_SHIFT) == -		    WLAN_HT_CAP_SM_PS_DYNAMIC) +		    sta->smps_mode == IEEE80211_SMPS_DYNAMIC)  			__set_bit(ENTRY_TXD_HT_MIMO_PS, &txdesc->flags);  	} else {  		txdesc->u.ht.mcs = rt2x00_get_rate_mcs(hwrate->mcs); @@ -545,8 +553,9 @@ static int rt2x00queue_write_tx_data(struct queue_entry *entry,  	/*  	 * Map the skb to DMA.  	 */ -	if (test_bit(REQUIRE_DMA, &rt2x00dev->cap_flags)) -		rt2x00queue_map_txskb(entry); +	if (test_bit(REQUIRE_DMA, &rt2x00dev->cap_flags) && +	    rt2x00queue_map_txskb(entry)) +		return -ENOMEM;  	return 0;  } @@ -582,6 +591,48 @@ static void rt2x00queue_kick_tx_queue(struct data_queue *queue,  		queue->rt2x00dev->ops->lib->kick_queue(queue);  } +static void rt2x00queue_bar_check(struct queue_entry *entry) +{ +	struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; +	struct ieee80211_bar *bar = (void *) (entry->skb->data + +				    rt2x00dev->ops->extra_tx_headroom); +	struct rt2x00_bar_list_entry *bar_entry; + +	if (likely(!ieee80211_is_back_req(bar->frame_control))) +		return; + +	bar_entry = kmalloc(sizeof(*bar_entry), GFP_ATOMIC); + +	/* +	 * If the alloc fails we still send the BAR out but just don't track +	 * it in our bar list. And as a result we will report it to mac80211 +	 * back as failed. +	 */ +	if (!bar_entry) +		return; + +	bar_entry->entry = entry; +	bar_entry->block_acked = 0; + +	/* +	 * Copy the relevant parts of the 802.11 BAR into out check list +	 * such that we can use RCU for less-overhead in the RX path since +	 * sending BARs and processing the according BlockAck should be +	 * the exception. +	 */ +	memcpy(bar_entry->ra, bar->ra, sizeof(bar->ra)); +	memcpy(bar_entry->ta, bar->ta, sizeof(bar->ta)); +	bar_entry->control = bar->control; +	bar_entry->start_seq_num = bar->start_seq_num; + +	/* +	 * Insert BAR into our BAR check list. +	 */ +	spin_lock_bh(&rt2x00dev->bar_list_lock); +	list_add_tail_rcu(&bar_entry->list, &rt2x00dev->bar_list); +	spin_unlock_bh(&rt2x00dev->bar_list_lock); +} +  int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb,  			       bool local)  { @@ -680,6 +731,11 @@ int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb,  		goto out;  	} +	/* +	 * Put BlockAckReqs into our check list for driver BA processing. +	 */ +	rt2x00queue_bar_check(entry); +  	set_bit(ENTRY_DATA_PENDING, &entry->flags);  	rt2x00queue_index_inc(entry, Q_INDEX);  |