summaryrefslogtreecommitdiff
path: root/drivers/net/wireless/rt2x00/rt2x00dev.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/rt2x00/rt2x00dev.c')
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00dev.c149
1 files changed, 127 insertions, 22 deletions
diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/rt2x00/rt2x00dev.c
index 3ae468c4d760..585e8166f22a 100644
--- a/drivers/net/wireless/rt2x00/rt2x00dev.c
+++ b/drivers/net/wireless/rt2x00/rt2x00dev.c
@@ -70,6 +70,11 @@ int rt2x00lib_enable_radio(struct rt2x00_dev *rt2x00dev)
rt2x00lib_toggle_rx(rt2x00dev, STATE_RADIO_RX_ON);
/*
+ * Start watchdog monitoring.
+ */
+ rt2x00link_start_watchdog(rt2x00dev);
+
+ /*
* Start the TX queues.
*/
ieee80211_wake_queues(rt2x00dev->hw);
@@ -89,6 +94,11 @@ void rt2x00lib_disable_radio(struct rt2x00_dev *rt2x00dev)
rt2x00queue_stop_queues(rt2x00dev);
/*
+ * Stop watchdog monitoring.
+ */
+ rt2x00link_stop_watchdog(rt2x00dev);
+
+ /*
* Disable RX.
*/
rt2x00lib_toggle_rx(rt2x00dev, STATE_RADIO_RX_OFF);
@@ -168,10 +178,32 @@ static void rt2x00lib_intf_scheduled(struct work_struct *work)
/*
* Interrupt context handlers.
*/
-static void rt2x00lib_beacondone_iter(void *data, u8 *mac,
- struct ieee80211_vif *vif)
+static void rt2x00lib_bc_buffer_iter(void *data, u8 *mac,
+ struct ieee80211_vif *vif)
{
- struct rt2x00_intf *intf = vif_to_intf(vif);
+ struct rt2x00_dev *rt2x00dev = data;
+ struct sk_buff *skb;
+
+ /*
+ * Only AP mode interfaces do broad- and multicast buffering
+ */
+ if (vif->type != NL80211_IFTYPE_AP)
+ return;
+
+ /*
+ * Send out buffered broad- and multicast frames
+ */
+ skb = ieee80211_get_buffered_bc(rt2x00dev->hw, vif);
+ while (skb) {
+ rt2x00mac_tx(rt2x00dev->hw, skb);
+ skb = ieee80211_get_buffered_bc(rt2x00dev->hw, vif);
+ }
+}
+
+static void rt2x00lib_beaconupdate_iter(void *data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ struct rt2x00_dev *rt2x00dev = data;
if (vif->type != NL80211_IFTYPE_AP &&
vif->type != NL80211_IFTYPE_ADHOC &&
@@ -179,9 +211,7 @@ static void rt2x00lib_beacondone_iter(void *data, u8 *mac,
vif->type != NL80211_IFTYPE_WDS)
return;
- spin_lock(&intf->lock);
- intf->delayed_flags |= DELAYED_UPDATE_BEACON;
- spin_unlock(&intf->lock);
+ rt2x00queue_update_beacon(rt2x00dev, vif, true);
}
void rt2x00lib_beacondone(struct rt2x00_dev *rt2x00dev)
@@ -189,14 +219,37 @@ void rt2x00lib_beacondone(struct rt2x00_dev *rt2x00dev)
if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
return;
- ieee80211_iterate_active_interfaces_atomic(rt2x00dev->hw,
- rt2x00lib_beacondone_iter,
- rt2x00dev);
+ /* send buffered bc/mc frames out for every bssid */
+ ieee80211_iterate_active_interfaces(rt2x00dev->hw,
+ rt2x00lib_bc_buffer_iter,
+ rt2x00dev);
+ /*
+ * Devices with pre tbtt interrupt don't need to update the beacon
+ * here as they will fetch the next beacon directly prior to
+ * transmission.
+ */
+ if (test_bit(DRIVER_SUPPORT_PRE_TBTT_INTERRUPT, &rt2x00dev->flags))
+ return;
- ieee80211_queue_work(rt2x00dev->hw, &rt2x00dev->intf_work);
+ /* fetch next beacon */
+ ieee80211_iterate_active_interfaces(rt2x00dev->hw,
+ rt2x00lib_beaconupdate_iter,
+ rt2x00dev);
}
EXPORT_SYMBOL_GPL(rt2x00lib_beacondone);
+void rt2x00lib_pretbtt(struct rt2x00_dev *rt2x00dev)
+{
+ if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
+ return;
+
+ /* fetch next beacon */
+ ieee80211_iterate_active_interfaces(rt2x00dev->hw,
+ rt2x00lib_beaconupdate_iter,
+ rt2x00dev);
+}
+EXPORT_SYMBOL_GPL(rt2x00lib_pretbtt);
+
void rt2x00lib_txdone(struct queue_entry *entry,
struct txdone_entry_desc *txdesc)
{
@@ -216,6 +269,16 @@ void rt2x00lib_txdone(struct queue_entry *entry,
rt2x00queue_unmap_skb(rt2x00dev, entry->skb);
/*
+ * Remove the extra tx headroom from the skb.
+ */
+ skb_pull(entry->skb, rt2x00dev->ops->extra_tx_headroom);
+
+ /*
+ * Signal that the TX descriptor is no longer in the skb.
+ */
+ skbdesc->flags &= ~SKBDESC_DESC_IN_SKB;
+
+ /*
* Remove L2 padding which was added during
*/
if (test_bit(DRIVER_REQUIRE_L2PAD, &rt2x00dev->flags))
@@ -224,7 +287,7 @@ void rt2x00lib_txdone(struct queue_entry *entry,
/*
* If the IV/EIV data was stripped from the frame before it was
* passed to the hardware, we should now reinsert it again because
- * mac80211 will expect the the same data to be present it the
+ * 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))
@@ -241,8 +304,7 @@ void rt2x00lib_txdone(struct queue_entry *entry,
*/
success =
test_bit(TXDONE_SUCCESS, &txdesc->flags) ||
- test_bit(TXDONE_UNKNOWN, &txdesc->flags) ||
- test_bit(TXDONE_FALLBACK, &txdesc->flags);
+ test_bit(TXDONE_UNKNOWN, &txdesc->flags);
/*
* Update TX statistics.
@@ -264,11 +326,22 @@ void rt2x00lib_txdone(struct queue_entry *entry,
/*
* Frame was send with retries, hardware tried
* different rates to send out the frame, at each
- * retry it lowered the rate 1 step.
+ * retry it lowered the rate 1 step except when the
+ * lowest rate was used.
*/
for (i = 0; i < retry_rates && i < IEEE80211_TX_MAX_RATES; i++) {
tx_info->status.rates[i].idx = rate_idx - i;
tx_info->status.rates[i].flags = rate_flags;
+
+ if (rate_idx - i == 0) {
+ /*
+ * The lowest rate (index 0) was used until the
+ * number of max retries was reached.
+ */
+ tx_info->status.rates[i].count = retry_rates - i;
+ i++;
+ break;
+ }
tx_info->status.rates[i].count = 1;
}
if (i < (IEEE80211_TX_MAX_RATES - 1))
@@ -281,6 +354,21 @@ void rt2x00lib_txdone(struct queue_entry *entry,
rt2x00dev->low_level_stats.dot11ACKFailureCount++;
}
+ /*
+ * Every single frame has it's own tx status, hence report
+ * every frame as ampdu of size 1.
+ *
+ * TODO: if we can find out how many frames were aggregated
+ * by the hw we could provide the real ampdu_len to mac80211
+ * which would allow the rc algorithm to better decide on
+ * which rates are suitable.
+ */
+ if (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 (rate_flags & IEEE80211_TX_RC_USE_RTS_CTS) {
if (success)
rt2x00dev->low_level_stats.dot11RTSSuccessCount++;
@@ -295,9 +383,17 @@ void rt2x00lib_txdone(struct queue_entry *entry,
* send the status report back.
*/
if (!(skbdesc_flags & SKBDESC_NOT_MAC80211))
- ieee80211_tx_status_irqsafe(rt2x00dev->hw, entry->skb);
+ /*
+ * 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);
else
- dev_kfree_skb_irq(entry->skb);
+ dev_kfree_skb_any(entry->skb);
/*
* Make this entry available for reuse.
@@ -444,7 +540,16 @@ void rt2x00lib_rxdone(struct rt2x00_dev *rt2x00dev,
*/
rt2x00debug_dump_frame(rt2x00dev, DUMP_FRAME_RXDONE, entry->skb);
memcpy(IEEE80211_SKB_RXCB(entry->skb), rx_status, sizeof(*rx_status));
- ieee80211_rx_irqsafe(rt2x00dev->hw, entry->skb);
+
+ /*
+ * 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);
/*
* Replace the skb with the freshly allocated one.
@@ -854,6 +959,11 @@ int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev)
BIT(NL80211_IFTYPE_WDS);
/*
+ * Initialize configuration work.
+ */
+ INIT_WORK(&rt2x00dev->intf_work, rt2x00lib_intf_scheduled);
+
+ /*
* Let the driver probe the device to detect the capabilities.
*/
retval = rt2x00dev->ops->lib->probe_hw(rt2x00dev);
@@ -863,11 +973,6 @@ int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev)
}
/*
- * Initialize configuration work.
- */
- INIT_WORK(&rt2x00dev->intf_work, rt2x00lib_intf_scheduled);
-
- /*
* Allocate queue array.
*/
retval = rt2x00queue_allocate(rt2x00dev);