aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/rt2x00/rt2800pci.c
diff options
context:
space:
mode:
authorHelmut Schaa <helmut.schaa@googlemail.com>2013-03-15 04:57:57 -0400
committerJohn W. Linville <linville@tuxdriver.com>2013-03-18 16:38:29 -0400
commit8857d6dc77e4e3afeee2f33c49597010130ed858 (patch)
tree578ceaaf88ea7ad9e439f48564f22a5eec16f7f8 /drivers/net/wireless/rt2x00/rt2800pci.c
parent1dd0dbb30eb8e5d3e855bc864e54b745d1b96fd6 (diff)
rt2x00: Fix tx status reporting for reordered frames in rt2800pci
rt2800 hardware sometimes reorders tx frames when transmitting to multiple BA enabled STAs concurrently. For example a tx queue [ STA1 | STA2 | STA1 | STA2 ] can result in the tx status reports [ STA1 | STA1 | STA2 | STA2 ] when the hw decides to put the frames for STA1 in one AMPDU. To mitigate this effect associate the currently processed tx status to the first frame in the tx queue with a matching wcid. This patch fixes several problems related to incorrect tx status reporting. Furthermore the tx rate selection is much more stable when communicating with multiple STAs. Signed-off-by: Helmut Schaa <helmut.schaa@googlemail.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/rt2x00/rt2800pci.c')
-rw-r--r--drivers/net/wireless/rt2x00/rt2800pci.c111
1 files changed, 108 insertions, 3 deletions
diff --git a/drivers/net/wireless/rt2x00/rt2800pci.c b/drivers/net/wireless/rt2x00/rt2800pci.c
index ded73da4de0b..80cf8d745c6f 100644
--- a/drivers/net/wireless/rt2x00/rt2800pci.c
+++ b/drivers/net/wireless/rt2x00/rt2800pci.c
@@ -742,10 +742,90 @@ static void rt2800pci_wakeup(struct rt2x00_dev *rt2x00dev)
742 rt2800_config(rt2x00dev, &libconf, IEEE80211_CONF_CHANGE_PS); 742 rt2800_config(rt2x00dev, &libconf, IEEE80211_CONF_CHANGE_PS);
743} 743}
744 744
745static bool rt2800pci_txdone_entry_check(struct queue_entry *entry, u32 status)
746{
747 __le32 *txwi;
748 u32 word;
749 int wcid, tx_wcid;
750
751 wcid = rt2x00_get_field32(status, TX_STA_FIFO_WCID);
752
753 txwi = rt2800_drv_get_txwi(entry);
754 rt2x00_desc_read(txwi, 1, &word);
755 tx_wcid = rt2x00_get_field32(word, TXWI_W1_WIRELESS_CLI_ID);
756
757 return (tx_wcid == wcid);
758}
759
760static bool rt2800pci_txdone_find_entry(struct queue_entry *entry, void *data)
761{
762 u32 status = *(u32 *)data;
763
764 /*
765 * rt2800pci hardware might reorder frames when exchanging traffic
766 * with multiple BA enabled STAs.
767 *
768 * For example, a tx queue
769 * [ STA1 | STA2 | STA1 | STA2 ]
770 * can result in tx status reports
771 * [ STA1 | STA1 | STA2 | STA2 ]
772 * when the hw decides to aggregate the frames for STA1 into one AMPDU.
773 *
774 * To mitigate this effect, associate the tx status to the first frame
775 * in the tx queue with a matching wcid.
776 */
777 if (rt2800pci_txdone_entry_check(entry, status) &&
778 !test_bit(ENTRY_DATA_STATUS_SET, &entry->flags)) {
779 /*
780 * Got a matching frame, associate the tx status with
781 * the frame
782 */
783 entry->status = status;
784 set_bit(ENTRY_DATA_STATUS_SET, &entry->flags);
785 return true;
786 }
787
788 /* Check the next frame */
789 return false;
790}
791
792static bool rt2800pci_txdone_match_first(struct queue_entry *entry, void *data)
793{
794 u32 status = *(u32 *)data;
795
796 /*
797 * Find the first frame without tx status and assign this status to it
798 * regardless if it matches or not.
799 */
800 if (!test_bit(ENTRY_DATA_STATUS_SET, &entry->flags)) {
801 /*
802 * Got a matching frame, associate the tx status with
803 * the frame
804 */
805 entry->status = status;
806 set_bit(ENTRY_DATA_STATUS_SET, &entry->flags);
807 return true;
808 }
809
810 /* Check the next frame */
811 return false;
812}
813static bool rt2800pci_txdone_release_entries(struct queue_entry *entry,
814 void *data)
815{
816 if (test_bit(ENTRY_DATA_STATUS_SET, &entry->flags)) {
817 rt2800_txdone_entry(entry, entry->status,
818 rt2800pci_get_txwi(entry));
819 return false;
820 }
821
822 /* No more frames to release */
823 return true;
824}
825
745static bool rt2800pci_txdone(struct rt2x00_dev *rt2x00dev) 826static bool rt2800pci_txdone(struct rt2x00_dev *rt2x00dev)
746{ 827{
747 struct data_queue *queue; 828 struct data_queue *queue;
748 struct queue_entry *entry;
749 u32 status; 829 u32 status;
750 u8 qid; 830 u8 qid;
751 int max_tx_done = 16; 831 int max_tx_done = 16;
@@ -783,8 +863,33 @@ static bool rt2800pci_txdone(struct rt2x00_dev *rt2x00dev)
783 break; 863 break;
784 } 864 }
785 865
786 entry = rt2x00queue_get_entry(queue, Q_INDEX_DONE); 866 /*
787 rt2800_txdone_entry(entry, status, rt2800pci_get_txwi(entry)); 867 * Let's associate this tx status with the first
868 * matching frame.
869 */
870 if (!rt2x00queue_for_each_entry(queue, Q_INDEX_DONE,
871 Q_INDEX, &status,
872 rt2800pci_txdone_find_entry)) {
873 /*
874 * We cannot match the tx status to any frame, so just
875 * use the first one.
876 */
877 if (!rt2x00queue_for_each_entry(queue, Q_INDEX_DONE,
878 Q_INDEX, &status,
879 rt2800pci_txdone_match_first)) {
880 WARNING(rt2x00dev, "No frame found for TX "
881 "status on queue %u, dropping\n",
882 qid);
883 break;
884 }
885 }
886
887 /*
888 * Release all frames with a valid tx status.
889 */
890 rt2x00queue_for_each_entry(queue, Q_INDEX_DONE,
891 Q_INDEX, NULL,
892 rt2800pci_txdone_release_entries);
788 893
789 if (--max_tx_done == 0) 894 if (--max_tx_done == 0)
790 break; 895 break;