diff options
Diffstat (limited to 'drivers/net/wireless/rt2x00/rt2x00dev.c')
| -rw-r--r-- | drivers/net/wireless/rt2x00/rt2x00dev.c | 192 | 
1 files changed, 118 insertions, 74 deletions
diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/rt2x00/rt2x00dev.c index 585e8166f22..5ba79b935f0 100644 --- a/drivers/net/wireless/rt2x00/rt2x00dev.c +++ b/drivers/net/wireless/rt2x00/rt2x00dev.c @@ -1,5 +1,6 @@  /* -	Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com> +	Copyright (C) 2010 Willow Garage <http://www.willowgarage.com> +	Copyright (C) 2004 - 2010 Ivo van Doorn <IvDoorn@gmail.com>  	<http://rt2x00.serialmonkey.com>  	This program is free software; you can redistribute it and/or modify @@ -250,6 +251,13 @@ void rt2x00lib_pretbtt(struct rt2x00_dev *rt2x00dev)  }  EXPORT_SYMBOL_GPL(rt2x00lib_pretbtt); +void rt2x00lib_dmadone(struct queue_entry *entry) +{ +	clear_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags); +	rt2x00queue_index_inc(entry->queue, Q_INDEX_DMA_DONE); +} +EXPORT_SYMBOL_GPL(rt2x00lib_dmadone); +  void rt2x00lib_txdone(struct queue_entry *entry,  		      struct txdone_entry_desc *txdesc)  { @@ -266,7 +274,7 @@ void rt2x00lib_txdone(struct queue_entry *entry,  	/*  	 * Unmap the skb.  	 */ -	rt2x00queue_unmap_skb(rt2x00dev, entry->skb); +	rt2x00queue_unmap_skb(entry);  	/*  	 * Remove the extra tx headroom from the skb. @@ -383,15 +391,7 @@ void rt2x00lib_txdone(struct queue_entry *entry,  	 * send the status report back.  	 */  	if (!(skbdesc_flags & SKBDESC_NOT_MAC80211)) -		/* -		 * Only PCI and SOC devices process the tx status in process -		 * context. Hence use ieee80211_tx_status for PCI and SOC -		 * devices and stick to ieee80211_tx_status_irqsafe for USB. -		 */ -		if (rt2x00_is_usb(rt2x00dev)) -			ieee80211_tx_status_irqsafe(rt2x00dev->hw, entry->skb); -		else -			ieee80211_tx_status(rt2x00dev->hw, entry->skb); +		ieee80211_tx_status(rt2x00dev->hw, entry->skb);  	else  		dev_kfree_skb_any(entry->skb); @@ -403,7 +403,6 @@ void rt2x00lib_txdone(struct queue_entry *entry,  	rt2x00dev->ops->lib->clear_entry(entry); -	clear_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags);  	rt2x00queue_index_inc(entry->queue, Q_INDEX_DONE);  	/* @@ -416,65 +415,89 @@ void rt2x00lib_txdone(struct queue_entry *entry,  }  EXPORT_SYMBOL_GPL(rt2x00lib_txdone); +void rt2x00lib_txdone_noinfo(struct queue_entry *entry, u32 status) +{ +	struct txdone_entry_desc txdesc; + +	txdesc.flags = 0; +	__set_bit(status, &txdesc.flags); +	txdesc.retry = 0; + +	rt2x00lib_txdone(entry, &txdesc); +} +EXPORT_SYMBOL_GPL(rt2x00lib_txdone_noinfo); +  static int rt2x00lib_rxdone_read_signal(struct rt2x00_dev *rt2x00dev,  					struct rxdone_entry_desc *rxdesc)  {  	struct ieee80211_supported_band *sband;  	const struct rt2x00_rate *rate;  	unsigned int i; -	int signal; -	int type; - -	/* -	 * For non-HT rates the MCS value needs to contain the -	 * actually used rate modulation (CCK or OFDM). -	 */ -	if (rxdesc->dev_flags & RXDONE_SIGNAL_MCS) -		signal = RATE_MCS(rxdesc->rate_mode, rxdesc->signal); -	else -		signal = rxdesc->signal; - -	type = (rxdesc->dev_flags & RXDONE_SIGNAL_MASK); +	int signal = rxdesc->signal; +	int type = (rxdesc->dev_flags & RXDONE_SIGNAL_MASK); -	sband = &rt2x00dev->bands[rt2x00dev->curr_band]; -	for (i = 0; i < sband->n_bitrates; i++) { -		rate = rt2x00_get_rate(sband->bitrates[i].hw_value); +	switch (rxdesc->rate_mode) { +	case RATE_MODE_CCK: +	case RATE_MODE_OFDM: +		/* +		 * For non-HT rates the MCS value needs to contain the +		 * actually used rate modulation (CCK or OFDM). +		 */ +		if (rxdesc->dev_flags & RXDONE_SIGNAL_MCS) +			signal = RATE_MCS(rxdesc->rate_mode, signal); -		if (((type == RXDONE_SIGNAL_PLCP) && -		     (rate->plcp == signal)) || -		    ((type == RXDONE_SIGNAL_BITRATE) && -		      (rate->bitrate == signal)) || -		    ((type == RXDONE_SIGNAL_MCS) && -		      (rate->mcs == signal))) { -			return i; +		sband = &rt2x00dev->bands[rt2x00dev->curr_band]; +		for (i = 0; i < sband->n_bitrates; i++) { +			rate = rt2x00_get_rate(sband->bitrates[i].hw_value); +			if (((type == RXDONE_SIGNAL_PLCP) && +			     (rate->plcp == signal)) || +			    ((type == RXDONE_SIGNAL_BITRATE) && +			      (rate->bitrate == signal)) || +			    ((type == RXDONE_SIGNAL_MCS) && +			      (rate->mcs == signal))) { +				return i; +			}  		} +		break; +	case RATE_MODE_HT_MIX: +	case RATE_MODE_HT_GREENFIELD: +		if (signal >= 0 && signal <= 76) +			return signal; +		break; +	default: +		break;  	}  	WARNING(rt2x00dev, "Frame received with unrecognized signal, " -		"signal=0x%.4x, type=%d.\n", signal, type); +		"mode=0x%.4x, signal=0x%.4x, type=%d.\n", +		rxdesc->rate_mode, signal, type);  	return 0;  } -void rt2x00lib_rxdone(struct rt2x00_dev *rt2x00dev, -		      struct queue_entry *entry) +void rt2x00lib_rxdone(struct queue_entry *entry)  { +	struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;  	struct rxdone_entry_desc rxdesc;  	struct sk_buff *skb; -	struct ieee80211_rx_status *rx_status = &rt2x00dev->rx_status; +	struct ieee80211_rx_status *rx_status;  	unsigned int header_length;  	int rate_idx; + +	if (test_bit(ENTRY_DATA_IO_FAILED, &entry->flags)) +		goto submit_entry; +  	/*  	 * Allocate a new sk_buffer. If no new buffer available, drop the  	 * received frame and reuse the existing buffer.  	 */ -	skb = rt2x00queue_alloc_rxskb(rt2x00dev, entry); +	skb = rt2x00queue_alloc_rxskb(entry);  	if (!skb) -		return; +		goto submit_entry;  	/*  	 * Unmap the skb.  	 */ -	rt2x00queue_unmap_skb(rt2x00dev, entry->skb); +	rt2x00queue_unmap_skb(entry);  	/*  	 * Extract the RXD details. @@ -509,57 +532,44 @@ void rt2x00lib_rxdone(struct rt2x00_dev *rt2x00dev,  	skb_trim(entry->skb, rxdesc.size);  	/* -	 * Check if the frame was received using HT. In that case, -	 * the rate is the MCS index and should be passed to mac80211 -	 * directly. Otherwise we need to translate the signal to -	 * the correct bitrate index. +	 * Translate the signal to the correct bitrate index.  	 */ -	if (rxdesc.rate_mode == RATE_MODE_CCK || -	    rxdesc.rate_mode == RATE_MODE_OFDM) { -		rate_idx = rt2x00lib_rxdone_read_signal(rt2x00dev, &rxdesc); -	} else { +	rate_idx = rt2x00lib_rxdone_read_signal(rt2x00dev, &rxdesc); +	if (rxdesc.rate_mode == RATE_MODE_HT_MIX || +	    rxdesc.rate_mode == RATE_MODE_HT_GREENFIELD)  		rxdesc.flags |= RX_FLAG_HT; -		rate_idx = rxdesc.signal; -	}  	/*  	 * Update extra components  	 */  	rt2x00link_update_stats(rt2x00dev, entry->skb, &rxdesc);  	rt2x00debug_update_crypto(rt2x00dev, &rxdesc); +	rt2x00debug_dump_frame(rt2x00dev, DUMP_FRAME_RXDONE, entry->skb); +	/* +	 * Initialize RX status information, and send frame +	 * to mac80211. +	 */ +	rx_status = IEEE80211_SKB_RXCB(entry->skb);  	rx_status->mactime = rxdesc.timestamp; +	rx_status->band = rt2x00dev->curr_band; +	rx_status->freq = rt2x00dev->curr_freq;  	rx_status->rate_idx = rate_idx;  	rx_status->signal = rxdesc.rssi;  	rx_status->flag = rxdesc.flags;  	rx_status->antenna = rt2x00dev->link.ant.active.rx; -	/* -	 * Send frame to mac80211 & debugfs. -	 * mac80211 will clean up the skb structure. -	 */ -	rt2x00debug_dump_frame(rt2x00dev, DUMP_FRAME_RXDONE, entry->skb); -	memcpy(IEEE80211_SKB_RXCB(entry->skb), rx_status, sizeof(*rx_status)); - -	/* -	 * Currently only PCI and SOC devices handle rx interrupts in process -	 * context. Hence, use ieee80211_rx_irqsafe for USB and ieee80211_rx_ni -	 * for PCI and SOC devices. -	 */ -	if (rt2x00_is_usb(rt2x00dev)) -		ieee80211_rx_irqsafe(rt2x00dev->hw, entry->skb); -	else -		ieee80211_rx_ni(rt2x00dev->hw, entry->skb); +	ieee80211_rx_ni(rt2x00dev->hw, entry->skb);  	/*  	 * Replace the skb with the freshly allocated one.  	 */  	entry->skb = skb; -	entry->flags = 0; +submit_entry:  	rt2x00dev->ops->lib->clear_entry(entry); -  	rt2x00queue_index_inc(entry->queue, Q_INDEX); +	rt2x00queue_index_inc(entry->queue, Q_INDEX_DONE);  }  EXPORT_SYMBOL_GPL(rt2x00lib_rxdone); @@ -710,7 +720,7 @@ static int rt2x00lib_probe_hw_modes(struct rt2x00_dev *rt2x00dev,  	for (i = 0; i < spec->num_channels; i++) {  		rt2x00lib_channel(&channels[i],  				  spec->channels[i].channel, -				  spec->channels_info[i].tx_power1, i); +				  spec->channels_info[i].max_power, i);  	}  	/* @@ -806,6 +816,30 @@ static int rt2x00lib_probe_hw(struct rt2x00_dev *rt2x00dev)  		rt2x00dev->hw->extra_tx_headroom += RT2X00_ALIGN_SIZE;  	/* +	 * Allocate tx status FIFO for driver use. +	 */ +	if (test_bit(DRIVER_REQUIRE_TXSTATUS_FIFO, &rt2x00dev->flags) && +	    rt2x00dev->ops->lib->txstatus_tasklet) { +		/* +		 * 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. +		 */ +		status = kfifo_alloc(&rt2x00dev->txstatus_fifo, 512, +				     GFP_KERNEL); +		if (status) +			return status; + +		/* tasklet for processing the tx status reports. */ +		tasklet_init(&rt2x00dev->txstatus_tasklet, +			     rt2x00dev->ops->lib->txstatus_tasklet, +			     (unsigned long)rt2x00dev); + +	} + +	/*  	 * Register HW.  	 */  	status = ieee80211_register_hw(rt2x00dev->hw); @@ -902,10 +936,8 @@ int rt2x00lib_start(struct rt2x00_dev *rt2x00dev)  	/* Enable the radio */  	retval = rt2x00lib_enable_radio(rt2x00dev); -	if (retval) { -		rt2x00queue_uninitialize(rt2x00dev); +	if (retval)  		return retval; -	}  	set_bit(DEVICE_STATE_STARTED, &rt2x00dev->flags); @@ -1017,6 +1049,18 @@ void rt2x00lib_remove_dev(struct rt2x00_dev *rt2x00dev)  	 * Stop all work.  	 */  	cancel_work_sync(&rt2x00dev->intf_work); +	cancel_work_sync(&rt2x00dev->rxdone_work); +	cancel_work_sync(&rt2x00dev->txdone_work); + +	/* +	 * Free the tx status fifo. +	 */ +	kfifo_free(&rt2x00dev->txstatus_fifo); + +	/* +	 * Kill the tx status tasklet. +	 */ +	tasklet_kill(&rt2x00dev->txstatus_tasklet);  	/*  	 * Uninitialize device.  |