diff options
-rw-r--r-- | drivers/net/wireless/rt2x00/rt2800lib.c | 6 | ||||
-rw-r--r-- | drivers/net/wireless/rt2x00/rt2x00.h | 20 | ||||
-rw-r--r-- | drivers/net/wireless/rt2x00/rt2x00dev.c | 101 | ||||
-rw-r--r-- | drivers/net/wireless/rt2x00/rt2x00queue.c | 47 |
4 files changed, 169 insertions, 5 deletions
diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c index f139a913c25a..a5c694f23d33 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/rt2x00/rt2800lib.c | |||
@@ -1296,8 +1296,7 @@ void rt2800_config_filter(struct rt2x00_dev *rt2x00dev, | |||
1296 | !(filter_flags & FIF_CONTROL)); | 1296 | !(filter_flags & FIF_CONTROL)); |
1297 | rt2x00_set_field32(®, RX_FILTER_CFG_DROP_PSPOLL, | 1297 | rt2x00_set_field32(®, RX_FILTER_CFG_DROP_PSPOLL, |
1298 | !(filter_flags & FIF_PSPOLL)); | 1298 | !(filter_flags & FIF_PSPOLL)); |
1299 | rt2x00_set_field32(®, RX_FILTER_CFG_DROP_BA, | 1299 | rt2x00_set_field32(®, RX_FILTER_CFG_DROP_BA, 0); |
1300 | !(filter_flags & FIF_CONTROL)); | ||
1301 | rt2x00_set_field32(®, RX_FILTER_CFG_DROP_BAR, | 1300 | rt2x00_set_field32(®, RX_FILTER_CFG_DROP_BAR, |
1302 | !(filter_flags & FIF_CONTROL)); | 1301 | !(filter_flags & FIF_CONTROL)); |
1303 | rt2x00_set_field32(®, RX_FILTER_CFG_DROP_CNTL, | 1302 | rt2x00_set_field32(®, RX_FILTER_CFG_DROP_CNTL, |
@@ -5146,8 +5145,7 @@ static int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev) | |||
5146 | IEEE80211_HW_SUPPORTS_PS | | 5145 | IEEE80211_HW_SUPPORTS_PS | |
5147 | IEEE80211_HW_PS_NULLFUNC_STACK | | 5146 | IEEE80211_HW_PS_NULLFUNC_STACK | |
5148 | IEEE80211_HW_AMPDU_AGGREGATION | | 5147 | IEEE80211_HW_AMPDU_AGGREGATION | |
5149 | IEEE80211_HW_REPORTS_TX_ACK_STATUS | | 5148 | IEEE80211_HW_REPORTS_TX_ACK_STATUS; |
5150 | IEEE80211_HW_TEARDOWN_AGGR_ON_BAR_FAIL; | ||
5151 | 5149 | ||
5152 | /* | 5150 | /* |
5153 | * Don't set IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING for USB devices | 5151 | * Don't set IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING for USB devices |
diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h index 0751b35ef6dc..b52512b8ac5f 100644 --- a/drivers/net/wireless/rt2x00/rt2x00.h +++ b/drivers/net/wireless/rt2x00/rt2x00.h | |||
@@ -1016,6 +1016,26 @@ struct rt2x00_dev { | |||
1016 | * Protect the interrupt mask register. | 1016 | * Protect the interrupt mask register. |
1017 | */ | 1017 | */ |
1018 | spinlock_t irqmask_lock; | 1018 | spinlock_t irqmask_lock; |
1019 | |||
1020 | /* | ||
1021 | * List of BlockAckReq TX entries that need driver BlockAck processing. | ||
1022 | */ | ||
1023 | struct list_head bar_list; | ||
1024 | spinlock_t bar_list_lock; | ||
1025 | }; | ||
1026 | |||
1027 | struct rt2x00_bar_list_entry { | ||
1028 | struct list_head list; | ||
1029 | struct rcu_head head; | ||
1030 | |||
1031 | struct queue_entry *entry; | ||
1032 | int block_acked; | ||
1033 | |||
1034 | /* Relevant parts of the IEEE80211 BAR header */ | ||
1035 | __u8 ra[6]; | ||
1036 | __u8 ta[6]; | ||
1037 | __le16 control; | ||
1038 | __le16 start_seq_num; | ||
1019 | }; | 1039 | }; |
1020 | 1040 | ||
1021 | /* | 1041 | /* |
diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/rt2x00/rt2x00dev.c index 44f8b3f3cbed..b40a53857498 100644 --- a/drivers/net/wireless/rt2x00/rt2x00dev.c +++ b/drivers/net/wireless/rt2x00/rt2x00dev.c | |||
@@ -271,6 +271,50 @@ void rt2x00lib_dmadone(struct queue_entry *entry) | |||
271 | } | 271 | } |
272 | EXPORT_SYMBOL_GPL(rt2x00lib_dmadone); | 272 | EXPORT_SYMBOL_GPL(rt2x00lib_dmadone); |
273 | 273 | ||
274 | static inline int rt2x00lib_txdone_bar_status(struct queue_entry *entry) | ||
275 | { | ||
276 | struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; | ||
277 | struct ieee80211_bar *bar = (void *) entry->skb->data; | ||
278 | struct rt2x00_bar_list_entry *bar_entry; | ||
279 | int ret; | ||
280 | |||
281 | if (likely(!ieee80211_is_back_req(bar->frame_control))) | ||
282 | return 0; | ||
283 | |||
284 | /* | ||
285 | * Unlike all other frames, the status report for BARs does | ||
286 | * not directly come from the hardware as it is incapable of | ||
287 | * matching a BA to a previously send BAR. The hardware will | ||
288 | * report all BARs as if they weren't acked at all. | ||
289 | * | ||
290 | * Instead the RX-path will scan for incoming BAs and set the | ||
291 | * block_acked flag if it sees one that was likely caused by | ||
292 | * a BAR from us. | ||
293 | * | ||
294 | * Remove remaining BARs here and return their status for | ||
295 | * TX done processing. | ||
296 | */ | ||
297 | ret = 0; | ||
298 | rcu_read_lock(); | ||
299 | list_for_each_entry_rcu(bar_entry, &rt2x00dev->bar_list, list) { | ||
300 | if (bar_entry->entry != entry) | ||
301 | continue; | ||
302 | |||
303 | spin_lock_bh(&rt2x00dev->bar_list_lock); | ||
304 | /* Return whether this BAR was blockacked or not */ | ||
305 | ret = bar_entry->block_acked; | ||
306 | /* Remove the BAR from our checklist */ | ||
307 | list_del_rcu(&bar_entry->list); | ||
308 | spin_unlock_bh(&rt2x00dev->bar_list_lock); | ||
309 | kfree_rcu(bar_entry, head); | ||
310 | |||
311 | break; | ||
312 | } | ||
313 | rcu_read_unlock(); | ||
314 | |||
315 | return ret; | ||
316 | } | ||
317 | |||
274 | void rt2x00lib_txdone(struct queue_entry *entry, | 318 | void rt2x00lib_txdone(struct queue_entry *entry, |
275 | struct txdone_entry_desc *txdesc) | 319 | struct txdone_entry_desc *txdesc) |
276 | { | 320 | { |
@@ -324,9 +368,12 @@ void rt2x00lib_txdone(struct queue_entry *entry, | |||
324 | rt2x00debug_dump_frame(rt2x00dev, DUMP_FRAME_TXDONE, entry->skb); | 368 | rt2x00debug_dump_frame(rt2x00dev, DUMP_FRAME_TXDONE, entry->skb); |
325 | 369 | ||
326 | /* | 370 | /* |
327 | * Determine if the frame has been successfully transmitted. | 371 | * Determine if the frame has been successfully transmitted and |
372 | * remove BARs from our check list while checking for their | ||
373 | * TX status. | ||
328 | */ | 374 | */ |
329 | success = | 375 | success = |
376 | rt2x00lib_txdone_bar_status(entry) || | ||
330 | test_bit(TXDONE_SUCCESS, &txdesc->flags) || | 377 | test_bit(TXDONE_SUCCESS, &txdesc->flags) || |
331 | test_bit(TXDONE_UNKNOWN, &txdesc->flags); | 378 | test_bit(TXDONE_UNKNOWN, &txdesc->flags); |
332 | 379 | ||
@@ -491,6 +538,50 @@ static void rt2x00lib_sleep(struct work_struct *work) | |||
491 | IEEE80211_CONF_CHANGE_PS); | 538 | IEEE80211_CONF_CHANGE_PS); |
492 | } | 539 | } |
493 | 540 | ||
541 | static void rt2x00lib_rxdone_check_ba(struct rt2x00_dev *rt2x00dev, | ||
542 | struct sk_buff *skb, | ||
543 | struct rxdone_entry_desc *rxdesc) | ||
544 | { | ||
545 | struct rt2x00_bar_list_entry *entry; | ||
546 | struct ieee80211_bar *ba = (void *)skb->data; | ||
547 | |||
548 | if (likely(!ieee80211_is_back(ba->frame_control))) | ||
549 | return; | ||
550 | |||
551 | if (rxdesc->size < sizeof(*ba) + FCS_LEN) | ||
552 | return; | ||
553 | |||
554 | rcu_read_lock(); | ||
555 | list_for_each_entry_rcu(entry, &rt2x00dev->bar_list, list) { | ||
556 | |||
557 | if (ba->start_seq_num != entry->start_seq_num) | ||
558 | continue; | ||
559 | |||
560 | #define TID_CHECK(a, b) ( \ | ||
561 | ((a) & cpu_to_le16(IEEE80211_BAR_CTRL_TID_INFO_MASK)) == \ | ||
562 | ((b) & cpu_to_le16(IEEE80211_BAR_CTRL_TID_INFO_MASK))) \ | ||
563 | |||
564 | if (!TID_CHECK(ba->control, entry->control)) | ||
565 | continue; | ||
566 | |||
567 | #undef TID_CHECK | ||
568 | |||
569 | if (compare_ether_addr(ba->ra, entry->ta)) | ||
570 | continue; | ||
571 | |||
572 | if (compare_ether_addr(ba->ta, entry->ra)) | ||
573 | continue; | ||
574 | |||
575 | /* Mark BAR since we received the according BA */ | ||
576 | spin_lock_bh(&rt2x00dev->bar_list_lock); | ||
577 | entry->block_acked = 1; | ||
578 | spin_unlock_bh(&rt2x00dev->bar_list_lock); | ||
579 | break; | ||
580 | } | ||
581 | rcu_read_unlock(); | ||
582 | |||
583 | } | ||
584 | |||
494 | static void rt2x00lib_rxdone_check_ps(struct rt2x00_dev *rt2x00dev, | 585 | static void rt2x00lib_rxdone_check_ps(struct rt2x00_dev *rt2x00dev, |
495 | struct sk_buff *skb, | 586 | struct sk_buff *skb, |
496 | struct rxdone_entry_desc *rxdesc) | 587 | struct rxdone_entry_desc *rxdesc) |
@@ -674,6 +765,12 @@ void rt2x00lib_rxdone(struct queue_entry *entry, gfp_t gfp) | |||
674 | rt2x00lib_rxdone_check_ps(rt2x00dev, entry->skb, &rxdesc); | 765 | rt2x00lib_rxdone_check_ps(rt2x00dev, entry->skb, &rxdesc); |
675 | 766 | ||
676 | /* | 767 | /* |
768 | * Check for incoming BlockAcks to match to the BlockAckReqs | ||
769 | * we've send out. | ||
770 | */ | ||
771 | rt2x00lib_rxdone_check_ba(rt2x00dev, entry->skb, &rxdesc); | ||
772 | |||
773 | /* | ||
677 | * Update extra components | 774 | * Update extra components |
678 | */ | 775 | */ |
679 | rt2x00link_update_stats(rt2x00dev, entry->skb, &rxdesc); | 776 | rt2x00link_update_stats(rt2x00dev, entry->skb, &rxdesc); |
@@ -1183,6 +1280,8 @@ int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev) | |||
1183 | 1280 | ||
1184 | spin_lock_init(&rt2x00dev->irqmask_lock); | 1281 | spin_lock_init(&rt2x00dev->irqmask_lock); |
1185 | mutex_init(&rt2x00dev->csr_mutex); | 1282 | mutex_init(&rt2x00dev->csr_mutex); |
1283 | INIT_LIST_HEAD(&rt2x00dev->bar_list); | ||
1284 | spin_lock_init(&rt2x00dev->bar_list_lock); | ||
1186 | 1285 | ||
1187 | set_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags); | 1286 | set_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags); |
1188 | 1287 | ||
diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.c b/drivers/net/wireless/rt2x00/rt2x00queue.c index e488b944a034..f35d85a71bbc 100644 --- a/drivers/net/wireless/rt2x00/rt2x00queue.c +++ b/drivers/net/wireless/rt2x00/rt2x00queue.c | |||
@@ -582,6 +582,48 @@ static void rt2x00queue_kick_tx_queue(struct data_queue *queue, | |||
582 | queue->rt2x00dev->ops->lib->kick_queue(queue); | 582 | queue->rt2x00dev->ops->lib->kick_queue(queue); |
583 | } | 583 | } |
584 | 584 | ||
585 | static void rt2x00queue_bar_check(struct queue_entry *entry) | ||
586 | { | ||
587 | struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; | ||
588 | struct ieee80211_bar *bar = (void *) (entry->skb->data + | ||
589 | rt2x00dev->ops->extra_tx_headroom); | ||
590 | struct rt2x00_bar_list_entry *bar_entry; | ||
591 | |||
592 | if (likely(!ieee80211_is_back_req(bar->frame_control))) | ||
593 | return; | ||
594 | |||
595 | bar_entry = kmalloc(sizeof(*bar_entry), GFP_ATOMIC); | ||
596 | |||
597 | /* | ||
598 | * If the alloc fails we still send the BAR out but just don't track | ||
599 | * it in our bar list. And as a result we will report it to mac80211 | ||
600 | * back as failed. | ||
601 | */ | ||
602 | if (!bar_entry) | ||
603 | return; | ||
604 | |||
605 | bar_entry->entry = entry; | ||
606 | bar_entry->block_acked = 0; | ||
607 | |||
608 | /* | ||
609 | * Copy the relevant parts of the 802.11 BAR into out check list | ||
610 | * such that we can use RCU for less-overhead in the RX path since | ||
611 | * sending BARs and processing the according BlockAck should be | ||
612 | * the exception. | ||
613 | */ | ||
614 | memcpy(bar_entry->ra, bar->ra, sizeof(bar->ra)); | ||
615 | memcpy(bar_entry->ta, bar->ta, sizeof(bar->ta)); | ||
616 | bar_entry->control = bar->control; | ||
617 | bar_entry->start_seq_num = bar->start_seq_num; | ||
618 | |||
619 | /* | ||
620 | * Insert BAR into our BAR check list. | ||
621 | */ | ||
622 | spin_lock_bh(&rt2x00dev->bar_list_lock); | ||
623 | list_add_tail_rcu(&bar_entry->list, &rt2x00dev->bar_list); | ||
624 | spin_unlock_bh(&rt2x00dev->bar_list_lock); | ||
625 | } | ||
626 | |||
585 | int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb, | 627 | int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb, |
586 | bool local) | 628 | bool local) |
587 | { | 629 | { |
@@ -680,6 +722,11 @@ int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb, | |||
680 | goto out; | 722 | goto out; |
681 | } | 723 | } |
682 | 724 | ||
725 | /* | ||
726 | * Put BlockAckReqs into our check list for driver BA processing. | ||
727 | */ | ||
728 | rt2x00queue_bar_check(entry); | ||
729 | |||
683 | set_bit(ENTRY_DATA_PENDING, &entry->flags); | 730 | set_bit(ENTRY_DATA_PENDING, &entry->flags); |
684 | 731 | ||
685 | rt2x00queue_index_inc(entry, Q_INDEX); | 732 | rt2x00queue_index_inc(entry, Q_INDEX); |