|
@@ -271,6 +271,50 @@ void rt2x00lib_dmadone(struct queue_entry *entry)
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(rt2x00lib_dmadone);
|
|
|
|
|
|
+static inline int rt2x00lib_txdone_bar_status(struct queue_entry *entry)
|
|
|
+{
|
|
|
+ struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
|
|
|
+ struct ieee80211_bar *bar = (void *) entry->skb->data;
|
|
|
+ struct rt2x00_bar_list_entry *bar_entry;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ if (likely(!ieee80211_is_back_req(bar->frame_control)))
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Unlike all other frames, the status report for BARs does
|
|
|
+ * not directly come from the hardware as it is incapable of
|
|
|
+ * matching a BA to a previously send BAR. The hardware will
|
|
|
+ * report all BARs as if they weren't acked at all.
|
|
|
+ *
|
|
|
+ * Instead the RX-path will scan for incoming BAs and set the
|
|
|
+ * block_acked flag if it sees one that was likely caused by
|
|
|
+ * a BAR from us.
|
|
|
+ *
|
|
|
+ * Remove remaining BARs here and return their status for
|
|
|
+ * TX done processing.
|
|
|
+ */
|
|
|
+ ret = 0;
|
|
|
+ rcu_read_lock();
|
|
|
+ list_for_each_entry_rcu(bar_entry, &rt2x00dev->bar_list, list) {
|
|
|
+ if (bar_entry->entry != entry)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ spin_lock_bh(&rt2x00dev->bar_list_lock);
|
|
|
+ /* Return whether this BAR was blockacked or not */
|
|
|
+ ret = bar_entry->block_acked;
|
|
|
+ /* Remove the BAR from our checklist */
|
|
|
+ list_del_rcu(&bar_entry->list);
|
|
|
+ spin_unlock_bh(&rt2x00dev->bar_list_lock);
|
|
|
+ kfree_rcu(bar_entry, head);
|
|
|
+
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ rcu_read_unlock();
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
void rt2x00lib_txdone(struct queue_entry *entry,
|
|
|
struct txdone_entry_desc *txdesc)
|
|
|
{
|
|
@@ -324,9 +368,12 @@ void rt2x00lib_txdone(struct queue_entry *entry,
|
|
|
rt2x00debug_dump_frame(rt2x00dev, DUMP_FRAME_TXDONE, entry->skb);
|
|
|
|
|
|
/*
|
|
|
- * Determine if the frame has been successfully transmitted.
|
|
|
+ * Determine if the frame has been successfully transmitted and
|
|
|
+ * remove BARs from our check list while checking for their
|
|
|
+ * TX status.
|
|
|
*/
|
|
|
success =
|
|
|
+ rt2x00lib_txdone_bar_status(entry) ||
|
|
|
test_bit(TXDONE_SUCCESS, &txdesc->flags) ||
|
|
|
test_bit(TXDONE_UNKNOWN, &txdesc->flags);
|
|
|
|
|
@@ -491,6 +538,50 @@ static void rt2x00lib_sleep(struct work_struct *work)
|
|
|
IEEE80211_CONF_CHANGE_PS);
|
|
|
}
|
|
|
|
|
|
+static void rt2x00lib_rxdone_check_ba(struct rt2x00_dev *rt2x00dev,
|
|
|
+ struct sk_buff *skb,
|
|
|
+ struct rxdone_entry_desc *rxdesc)
|
|
|
+{
|
|
|
+ struct rt2x00_bar_list_entry *entry;
|
|
|
+ struct ieee80211_bar *ba = (void *)skb->data;
|
|
|
+
|
|
|
+ if (likely(!ieee80211_is_back(ba->frame_control)))
|
|
|
+ return;
|
|
|
+
|
|
|
+ if (rxdesc->size < sizeof(*ba) + FCS_LEN)
|
|
|
+ return;
|
|
|
+
|
|
|
+ rcu_read_lock();
|
|
|
+ list_for_each_entry_rcu(entry, &rt2x00dev->bar_list, list) {
|
|
|
+
|
|
|
+ if (ba->start_seq_num != entry->start_seq_num)
|
|
|
+ continue;
|
|
|
+
|
|
|
+#define TID_CHECK(a, b) ( \
|
|
|
+ ((a) & cpu_to_le16(IEEE80211_BAR_CTRL_TID_INFO_MASK)) == \
|
|
|
+ ((b) & cpu_to_le16(IEEE80211_BAR_CTRL_TID_INFO_MASK))) \
|
|
|
+
|
|
|
+ if (!TID_CHECK(ba->control, entry->control))
|
|
|
+ continue;
|
|
|
+
|
|
|
+#undef TID_CHECK
|
|
|
+
|
|
|
+ if (compare_ether_addr(ba->ra, entry->ta))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ if (compare_ether_addr(ba->ta, entry->ra))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ /* Mark BAR since we received the according BA */
|
|
|
+ spin_lock_bh(&rt2x00dev->bar_list_lock);
|
|
|
+ entry->block_acked = 1;
|
|
|
+ spin_unlock_bh(&rt2x00dev->bar_list_lock);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ rcu_read_unlock();
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
static void rt2x00lib_rxdone_check_ps(struct rt2x00_dev *rt2x00dev,
|
|
|
struct sk_buff *skb,
|
|
|
struct rxdone_entry_desc *rxdesc)
|
|
@@ -673,6 +764,12 @@ void rt2x00lib_rxdone(struct queue_entry *entry, gfp_t gfp)
|
|
|
*/
|
|
|
rt2x00lib_rxdone_check_ps(rt2x00dev, entry->skb, &rxdesc);
|
|
|
|
|
|
+ /*
|
|
|
+ * Check for incoming BlockAcks to match to the BlockAckReqs
|
|
|
+ * we've send out.
|
|
|
+ */
|
|
|
+ rt2x00lib_rxdone_check_ba(rt2x00dev, entry->skb, &rxdesc);
|
|
|
+
|
|
|
/*
|
|
|
* Update extra components
|
|
|
*/
|
|
@@ -1183,6 +1280,8 @@ int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev)
|
|
|
|
|
|
spin_lock_init(&rt2x00dev->irqmask_lock);
|
|
|
mutex_init(&rt2x00dev->csr_mutex);
|
|
|
+ INIT_LIST_HEAD(&rt2x00dev->bar_list);
|
|
|
+ spin_lock_init(&rt2x00dev->bar_list_lock);
|
|
|
|
|
|
set_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags);
|
|
|
|