diff options
Diffstat (limited to 'drivers/net/wireless/rt2x00/rt2x00dev.c')
| -rw-r--r-- | drivers/net/wireless/rt2x00/rt2x00dev.c | 139 | 
1 files changed, 119 insertions, 20 deletions
diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/rt2x00/rt2x00dev.c index 84eb6ad3637..c018d67aab8 100644 --- a/drivers/net/wireless/rt2x00/rt2x00dev.c +++ b/drivers/net/wireless/rt2x00/rt2x00dev.c @@ -27,6 +27,7 @@  #include <linux/kernel.h>  #include <linux/module.h>  #include <linux/slab.h> +#include <linux/log2.h>  #include "rt2x00.h"  #include "rt2x00lib.h" @@ -70,6 +71,7 @@ int rt2x00lib_enable_radio(struct rt2x00_dev *rt2x00dev)  	 */  	rt2x00queue_start_queues(rt2x00dev);  	rt2x00link_start_tuner(rt2x00dev); +	rt2x00link_start_agc(rt2x00dev);  	/*  	 * Start watchdog monitoring. @@ -92,6 +94,7 @@ void rt2x00lib_disable_radio(struct rt2x00_dev *rt2x00dev)  	/*  	 * Stop all queues  	 */ +	rt2x00link_stop_agc(rt2x00dev);  	rt2x00link_stop_tuner(rt2x00dev);  	rt2x00queue_stop_queues(rt2x00dev);  	rt2x00queue_flush_queues(rt2x00dev, true); @@ -138,6 +141,16 @@ static void rt2x00lib_intf_scheduled(struct work_struct *work)  					    rt2x00dev);  } +static void rt2x00lib_autowakeup(struct work_struct *work) +{ +	struct rt2x00_dev *rt2x00dev = +	    container_of(work, struct rt2x00_dev, autowakeup_work.work); + +	if (rt2x00dev->ops->lib->set_device_state(rt2x00dev, STATE_AWAKE)) +		ERROR(rt2x00dev, "Device failed to wakeup.\n"); +	clear_bit(CONFIG_POWERSAVING, &rt2x00dev->flags); +} +  /*   * Interrupt context handlers.   */ @@ -197,7 +210,7 @@ void rt2x00lib_beacondone(struct rt2x00_dev *rt2x00dev)  	 * here as they will fetch the next beacon directly prior to  	 * transmission.  	 */ -	if (test_bit(DRIVER_SUPPORT_PRE_TBTT_INTERRUPT, &rt2x00dev->flags)) +	if (test_bit(CAPABILITY_PRE_TBTT_INTERRUPT, &rt2x00dev->cap_flags))  		return;  	/* fetch next beacon */ @@ -222,7 +235,7 @@ EXPORT_SYMBOL_GPL(rt2x00lib_pretbtt);  void rt2x00lib_dmastart(struct queue_entry *entry)  {  	set_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags); -	rt2x00queue_index_inc(entry->queue, Q_INDEX); +	rt2x00queue_index_inc(entry, Q_INDEX);  }  EXPORT_SYMBOL_GPL(rt2x00lib_dmastart); @@ -230,7 +243,7 @@ void rt2x00lib_dmadone(struct queue_entry *entry)  {  	set_bit(ENTRY_DATA_STATUS_PENDING, &entry->flags);  	clear_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags); -	rt2x00queue_index_inc(entry->queue, Q_INDEX_DMA_DONE); +	rt2x00queue_index_inc(entry, Q_INDEX_DMA_DONE);  }  EXPORT_SYMBOL_GPL(rt2x00lib_dmadone); @@ -268,7 +281,7 @@ void rt2x00lib_txdone(struct queue_entry *entry,  	/*  	 * Remove L2 padding which was added during  	 */ -	if (test_bit(DRIVER_REQUIRE_L2PAD, &rt2x00dev->flags)) +	if (test_bit(REQUIRE_L2PAD, &rt2x00dev->cap_flags))  		rt2x00queue_remove_l2pad(entry->skb, header_length);  	/* @@ -277,7 +290,7 @@ void rt2x00lib_txdone(struct queue_entry *entry,  	 * mac80211 will expect the same data to be present it the  	 * frame as it was passed to us.  	 */ -	if (test_bit(CONFIG_SUPPORT_HW_CRYPTO, &rt2x00dev->flags)) +	if (test_bit(CAPABILITY_HW_CRYPTO, &rt2x00dev->cap_flags))  		rt2x00crypto_tx_insert_iv(entry->skb, header_length);  	/* @@ -350,10 +363,14 @@ void rt2x00lib_txdone(struct queue_entry *entry,  	 * which would allow the rc algorithm to better decide on  	 * which rates are suitable.  	 */ -	if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) { +	if (test_bit(TXDONE_AMPDU, &txdesc->flags) || +	    tx_info->flags & IEEE80211_TX_CTL_AMPDU) {  		tx_info->flags |= IEEE80211_TX_STAT_AMPDU;  		tx_info->status.ampdu_len = 1;  		tx_info->status.ampdu_ack_len = success ? 1 : 0; + +		if (!success) +			tx_info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK;  	}  	if (rate_flags & IEEE80211_TX_RC_USE_RTS_CTS) { @@ -370,7 +387,7 @@ void rt2x00lib_txdone(struct queue_entry *entry,  	 * send the status report back.  	 */  	if (!(skbdesc_flags & SKBDESC_NOT_MAC80211)) { -		if (test_bit(DRIVER_REQUIRE_TASKLET_CONTEXT, &rt2x00dev->flags)) +		if (test_bit(REQUIRE_TASKLET_CONTEXT, &rt2x00dev->cap_flags))  			ieee80211_tx_status(rt2x00dev->hw, entry->skb);  		else  			ieee80211_tx_status_ni(rt2x00dev->hw, entry->skb); @@ -385,7 +402,7 @@ void rt2x00lib_txdone(struct queue_entry *entry,  	rt2x00dev->ops->lib->clear_entry(entry); -	rt2x00queue_index_inc(entry->queue, Q_INDEX_DONE); +	rt2x00queue_index_inc(entry, Q_INDEX_DONE);  	/*  	 * If the data queue was below the threshold before the txdone @@ -409,6 +426,77 @@ void rt2x00lib_txdone_noinfo(struct queue_entry *entry, u32 status)  }  EXPORT_SYMBOL_GPL(rt2x00lib_txdone_noinfo); +static u8 *rt2x00lib_find_ie(u8 *data, unsigned int len, u8 ie) +{ +	struct ieee80211_mgmt *mgmt = (void *)data; +	u8 *pos, *end; + +	pos = (u8 *)mgmt->u.beacon.variable; +	end = data + len; +	while (pos < end) { +		if (pos + 2 + pos[1] > end) +			return NULL; + +		if (pos[0] == ie) +			return pos; + +		pos += 2 + pos[1]; +	} + +	return NULL; +} + +static void rt2x00lib_rxdone_check_ps(struct rt2x00_dev *rt2x00dev, +				      struct sk_buff *skb, +				      struct rxdone_entry_desc *rxdesc) +{ +	struct ieee80211_hdr *hdr = (void *) skb->data; +	struct ieee80211_tim_ie *tim_ie; +	u8 *tim; +	u8 tim_len; +	bool cam; + +	/* If this is not a beacon, or if mac80211 has no powersaving +	 * configured, or if the device is already in powersaving mode +	 * we can exit now. */ +	if (likely(!ieee80211_is_beacon(hdr->frame_control) || +		   !(rt2x00dev->hw->conf.flags & IEEE80211_CONF_PS))) +		return; + +	/* min. beacon length + FCS_LEN */ +	if (skb->len <= 40 + FCS_LEN) +		return; + +	/* and only beacons from the associated BSSID, please */ +	if (!(rxdesc->dev_flags & RXDONE_MY_BSS) || +	    !rt2x00dev->aid) +		return; + +	rt2x00dev->last_beacon = jiffies; + +	tim = rt2x00lib_find_ie(skb->data, skb->len - FCS_LEN, WLAN_EID_TIM); +	if (!tim) +		return; + +	if (tim[1] < sizeof(*tim_ie)) +		return; + +	tim_len = tim[1]; +	tim_ie = (struct ieee80211_tim_ie *) &tim[2]; + +	/* Check whenever the PHY can be turned off again. */ + +	/* 1. What about buffered unicast traffic for our AID? */ +	cam = ieee80211_check_tim(tim_ie, tim_len, rt2x00dev->aid); + +	/* 2. Maybe the AP wants to send multicast/broadcast data? */ +	cam |= (tim_ie->bitmap_ctrl & 0x01); + +	if (!cam && !test_bit(CONFIG_POWERSAVING, &rt2x00dev->flags)) +		rt2x00lib_config(rt2x00dev, &rt2x00dev->hw->conf, +				 IEEE80211_CONF_CHANGE_PS); +} +  static int rt2x00lib_rxdone_read_signal(struct rt2x00_dev *rt2x00dev,  					struct rxdone_entry_desc *rxdesc)  { @@ -511,8 +599,6 @@ void rt2x00lib_rxdone(struct queue_entry *entry)  		 (rxdesc.size > header_length) &&  		 (rxdesc.dev_flags & RXDONE_L2PAD))  		rt2x00queue_remove_l2pad(entry->skb, header_length); -	else -		rt2x00queue_align_payload(entry->skb, header_length);  	/* Trim buffer to correct size */  	skb_trim(entry->skb, rxdesc.size); @@ -526,6 +612,12 @@ void rt2x00lib_rxdone(struct queue_entry *entry)  		rxdesc.flags |= RX_FLAG_HT;  	/* +	 * Check if this is a beacon, and more frames have been +	 * buffered while we were in powersaving mode. +	 */ +	rt2x00lib_rxdone_check_ps(rt2x00dev, entry->skb, &rxdesc); + +	/*  	 * Update extra components  	 */  	rt2x00link_update_stats(rt2x00dev, entry->skb, &rxdesc); @@ -554,7 +646,7 @@ void rt2x00lib_rxdone(struct queue_entry *entry)  submit_entry:  	entry->flags = 0; -	rt2x00queue_index_inc(entry->queue, Q_INDEX_DONE); +	rt2x00queue_index_inc(entry, Q_INDEX_DONE);  	if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags) &&  	    test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))  		rt2x00dev->ops->lib->clear_entry(entry); @@ -801,23 +893,28 @@ static int rt2x00lib_probe_hw(struct rt2x00_dev *rt2x00dev)  	/*  	 * Take TX headroom required for alignment into account.  	 */ -	if (test_bit(DRIVER_REQUIRE_L2PAD, &rt2x00dev->flags)) +	if (test_bit(REQUIRE_L2PAD, &rt2x00dev->cap_flags))  		rt2x00dev->hw->extra_tx_headroom += RT2X00_L2PAD_SIZE; -	else if (test_bit(DRIVER_REQUIRE_DMA, &rt2x00dev->flags)) +	else if (test_bit(REQUIRE_DMA, &rt2x00dev->cap_flags))  		rt2x00dev->hw->extra_tx_headroom += RT2X00_ALIGN_SIZE;  	/*  	 * Allocate tx status FIFO for driver use.  	 */ -	if (test_bit(DRIVER_REQUIRE_TXSTATUS_FIFO, &rt2x00dev->flags)) { +	if (test_bit(REQUIRE_TXSTATUS_FIFO, &rt2x00dev->cap_flags)) {  		/* -		 * Allocate txstatus fifo and tasklet, we use a size of 512 -		 * for the kfifo which is big enough to store 512/4=128 tx -		 * status reports. In the worst case (tx status for all tx -		 * queues gets reported before we've got a chance to handle -		 * them) 24*4=384 tx status reports need to be cached. +		 * Allocate the txstatus fifo. In the worst case the tx +		 * status fifo has to hold the tx status of all entries +		 * in all tx queues. Hence, calculate the kfifo size as +		 * tx_queues * entry_num and round up to the nearest +		 * power of 2.  		 */ -		status = kfifo_alloc(&rt2x00dev->txstatus_fifo, 512, +		int kfifo_size = +			roundup_pow_of_two(rt2x00dev->ops->tx_queues * +					   rt2x00dev->ops->tx->entry_num * +					   sizeof(u32)); + +		status = kfifo_alloc(&rt2x00dev->txstatus_fifo, kfifo_size,  				     GFP_KERNEL);  		if (status)  			return status; @@ -1007,6 +1104,7 @@ int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev)  	}  	INIT_WORK(&rt2x00dev->intf_work, rt2x00lib_intf_scheduled); +	INIT_DELAYED_WORK(&rt2x00dev->autowakeup_work, rt2x00lib_autowakeup);  	/*  	 * Let the driver probe the device to detect the capabilities. @@ -1063,6 +1161,7 @@ void rt2x00lib_remove_dev(struct rt2x00_dev *rt2x00dev)  	 */  	cancel_work_sync(&rt2x00dev->intf_work);  	if (rt2x00_is_usb(rt2x00dev)) { +		del_timer_sync(&rt2x00dev->txstatus_timer);  		cancel_work_sync(&rt2x00dev->rxdone_work);  		cancel_work_sync(&rt2x00dev->txdone_work);  	}  |