diff options
author | Nishant Sarmukadam <nishants@marvell.com> | 2011-03-17 14:58:46 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2011-03-30 14:15:13 -0400 |
commit | 65f3ddcd08fe24490359274a8c9bf526e81357a5 (patch) | |
tree | ce6242a2c0a2eb179fb4b78f22b84e378e11f72b | |
parent | ac109fd0427008e5b55e0e52e59c364de6d686fe (diff) |
mwl8k: Initiate BA sessions
Specifically, handle ampdu_action and attempt to start a BA
session on receiving the first qos packet from mac80211 for
transmission to a HT sta. While the BA session is being created,
all the packets belonging to that stream will be dropped to
prevent sequence number mismatch at the recipient.
Contains contributions from:
Yogesh Powar <yogeshp@marvell.com>
Pradeep Nemavat <pnemavat@marvell.com>
Brian Cavagnolo <brian@cozybit.com>
Signed-off-by: Brian Cavagnolo <brian@cozybit.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
-rw-r--r-- | drivers/net/wireless/mwl8k.c | 194 |
1 files changed, 187 insertions, 7 deletions
diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index dcd4508b1fd..ec1190ab0f8 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c | |||
@@ -1577,7 +1577,8 @@ mwl8k_txq_reclaim(struct ieee80211_hw *hw, int index, int limit, int force) | |||
1577 | processed++; | 1577 | processed++; |
1578 | } | 1578 | } |
1579 | 1579 | ||
1580 | if (processed && priv->radio_on && !mutex_is_locked(&priv->fw_mutex)) | 1580 | if (index < MWL8K_TX_WMM_QUEUES && processed && priv->radio_on && |
1581 | !mutex_is_locked(&priv->fw_mutex)) | ||
1581 | ieee80211_wake_queue(hw, index); | 1582 | ieee80211_wake_queue(hw, index); |
1582 | 1583 | ||
1583 | return processed; | 1584 | return processed; |
@@ -1677,6 +1678,7 @@ mwl8k_txq_xmit(struct ieee80211_hw *hw, int index, struct sk_buff *skb) | |||
1677 | struct mwl8k_priv *priv = hw->priv; | 1678 | struct mwl8k_priv *priv = hw->priv; |
1678 | struct ieee80211_tx_info *tx_info; | 1679 | struct ieee80211_tx_info *tx_info; |
1679 | struct mwl8k_vif *mwl8k_vif; | 1680 | struct mwl8k_vif *mwl8k_vif; |
1681 | struct ieee80211_sta *sta; | ||
1680 | struct ieee80211_hdr *wh; | 1682 | struct ieee80211_hdr *wh; |
1681 | struct mwl8k_tx_queue *txq; | 1683 | struct mwl8k_tx_queue *txq; |
1682 | struct mwl8k_tx_desc *tx; | 1684 | struct mwl8k_tx_desc *tx; |
@@ -1684,6 +1686,10 @@ mwl8k_txq_xmit(struct ieee80211_hw *hw, int index, struct sk_buff *skb) | |||
1684 | u32 txstatus; | 1686 | u32 txstatus; |
1685 | u8 txdatarate; | 1687 | u8 txdatarate; |
1686 | u16 qos; | 1688 | u16 qos; |
1689 | int txpriority; | ||
1690 | u8 tid = 0; | ||
1691 | struct mwl8k_ampdu_stream *stream = NULL; | ||
1692 | bool start_ba_session = false; | ||
1687 | 1693 | ||
1688 | wh = (struct ieee80211_hdr *)skb->data; | 1694 | wh = (struct ieee80211_hdr *)skb->data; |
1689 | if (ieee80211_is_data_qos(wh->frame_control)) | 1695 | if (ieee80211_is_data_qos(wh->frame_control)) |
@@ -1699,6 +1705,7 @@ mwl8k_txq_xmit(struct ieee80211_hw *hw, int index, struct sk_buff *skb) | |||
1699 | wh = &((struct mwl8k_dma_data *)skb->data)->wh; | 1705 | wh = &((struct mwl8k_dma_data *)skb->data)->wh; |
1700 | 1706 | ||
1701 | tx_info = IEEE80211_SKB_CB(skb); | 1707 | tx_info = IEEE80211_SKB_CB(skb); |
1708 | sta = tx_info->control.sta; | ||
1702 | mwl8k_vif = MWL8K_VIF(tx_info->control.vif); | 1709 | mwl8k_vif = MWL8K_VIF(tx_info->control.vif); |
1703 | 1710 | ||
1704 | if (tx_info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) { | 1711 | if (tx_info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) { |
@@ -1726,12 +1733,70 @@ mwl8k_txq_xmit(struct ieee80211_hw *hw, int index, struct sk_buff *skb) | |||
1726 | qos |= MWL8K_QOS_ACK_POLICY_NORMAL; | 1733 | qos |= MWL8K_QOS_ACK_POLICY_NORMAL; |
1727 | } | 1734 | } |
1728 | 1735 | ||
1736 | txpriority = index; | ||
1737 | |||
1738 | if (ieee80211_is_data_qos(wh->frame_control) && | ||
1739 | skb->protocol != cpu_to_be16(ETH_P_PAE) && | ||
1740 | sta->ht_cap.ht_supported && priv->ap_fw) { | ||
1741 | tid = qos & 0xf; | ||
1742 | spin_lock(&priv->stream_lock); | ||
1743 | stream = mwl8k_lookup_stream(hw, sta->addr, tid); | ||
1744 | if (stream != NULL) { | ||
1745 | if (stream->state == AMPDU_STREAM_ACTIVE) { | ||
1746 | txpriority = stream->txq_idx; | ||
1747 | index = stream->txq_idx; | ||
1748 | } else if (stream->state == AMPDU_STREAM_NEW) { | ||
1749 | /* We get here if the driver sends us packets | ||
1750 | * after we've initiated a stream, but before | ||
1751 | * our ampdu_action routine has been called | ||
1752 | * with IEEE80211_AMPDU_TX_START to get the SSN | ||
1753 | * for the ADDBA request. So this packet can | ||
1754 | * go out with no risk of sequence number | ||
1755 | * mismatch. No special handling is required. | ||
1756 | */ | ||
1757 | } else { | ||
1758 | /* Drop packets that would go out after the | ||
1759 | * ADDBA request was sent but before the ADDBA | ||
1760 | * response is received. If we don't do this, | ||
1761 | * the recipient would probably receive it | ||
1762 | * after the ADDBA request with SSN 0. This | ||
1763 | * will cause the recipient's BA receive window | ||
1764 | * to shift, which would cause the subsequent | ||
1765 | * packets in the BA stream to be discarded. | ||
1766 | * mac80211 queues our packets for us in this | ||
1767 | * case, so this is really just a safety check. | ||
1768 | */ | ||
1769 | wiphy_warn(hw->wiphy, | ||
1770 | "Cannot send packet while ADDBA " | ||
1771 | "dialog is underway.\n"); | ||
1772 | spin_unlock(&priv->stream_lock); | ||
1773 | dev_kfree_skb(skb); | ||
1774 | return; | ||
1775 | } | ||
1776 | } else { | ||
1777 | /* Defer calling mwl8k_start_stream so that the current | ||
1778 | * skb can go out before the ADDBA request. This | ||
1779 | * prevents sequence number mismatch at the recepient | ||
1780 | * as described above. | ||
1781 | */ | ||
1782 | stream = mwl8k_add_stream(hw, sta, tid); | ||
1783 | if (stream != NULL) | ||
1784 | start_ba_session = true; | ||
1785 | } | ||
1786 | spin_unlock(&priv->stream_lock); | ||
1787 | } | ||
1788 | |||
1729 | dma = pci_map_single(priv->pdev, skb->data, | 1789 | dma = pci_map_single(priv->pdev, skb->data, |
1730 | skb->len, PCI_DMA_TODEVICE); | 1790 | skb->len, PCI_DMA_TODEVICE); |
1731 | 1791 | ||
1732 | if (pci_dma_mapping_error(priv->pdev, dma)) { | 1792 | if (pci_dma_mapping_error(priv->pdev, dma)) { |
1733 | wiphy_debug(hw->wiphy, | 1793 | wiphy_debug(hw->wiphy, |
1734 | "failed to dma map skb, dropping TX frame.\n"); | 1794 | "failed to dma map skb, dropping TX frame.\n"); |
1795 | if (start_ba_session) { | ||
1796 | spin_lock(&priv->stream_lock); | ||
1797 | mwl8k_remove_stream(hw, stream); | ||
1798 | spin_unlock(&priv->stream_lock); | ||
1799 | } | ||
1735 | dev_kfree_skb(skb); | 1800 | dev_kfree_skb(skb); |
1736 | return; | 1801 | return; |
1737 | } | 1802 | } |
@@ -1740,12 +1805,22 @@ mwl8k_txq_xmit(struct ieee80211_hw *hw, int index, struct sk_buff *skb) | |||
1740 | 1805 | ||
1741 | txq = priv->txq + index; | 1806 | txq = priv->txq + index; |
1742 | 1807 | ||
1808 | if (index >= MWL8K_TX_WMM_QUEUES && txq->len >= MWL8K_TX_DESCS) { | ||
1809 | /* This is the case in which the tx packet is destined for an | ||
1810 | * AMPDU queue and that AMPDU queue is full. Because we don't | ||
1811 | * start and stop the AMPDU queues, we must drop these packets. | ||
1812 | */ | ||
1813 | dev_kfree_skb(skb); | ||
1814 | spin_unlock_bh(&priv->tx_lock); | ||
1815 | return; | ||
1816 | } | ||
1817 | |||
1743 | BUG_ON(txq->skb[txq->tail] != NULL); | 1818 | BUG_ON(txq->skb[txq->tail] != NULL); |
1744 | txq->skb[txq->tail] = skb; | 1819 | txq->skb[txq->tail] = skb; |
1745 | 1820 | ||
1746 | tx = txq->txd + txq->tail; | 1821 | tx = txq->txd + txq->tail; |
1747 | tx->data_rate = txdatarate; | 1822 | tx->data_rate = txdatarate; |
1748 | tx->tx_priority = index; | 1823 | tx->tx_priority = txpriority; |
1749 | tx->qos_control = cpu_to_le16(qos); | 1824 | tx->qos_control = cpu_to_le16(qos); |
1750 | tx->pkt_phys_addr = cpu_to_le32(dma); | 1825 | tx->pkt_phys_addr = cpu_to_le32(dma); |
1751 | tx->pkt_len = cpu_to_le16(skb->len); | 1826 | tx->pkt_len = cpu_to_le16(skb->len); |
@@ -1764,12 +1839,20 @@ mwl8k_txq_xmit(struct ieee80211_hw *hw, int index, struct sk_buff *skb) | |||
1764 | if (txq->tail == MWL8K_TX_DESCS) | 1839 | if (txq->tail == MWL8K_TX_DESCS) |
1765 | txq->tail = 0; | 1840 | txq->tail = 0; |
1766 | 1841 | ||
1767 | if (txq->head == txq->tail) | 1842 | if (txq->head == txq->tail && index < MWL8K_TX_WMM_QUEUES) |
1768 | ieee80211_stop_queue(hw, index); | 1843 | ieee80211_stop_queue(hw, index); |
1769 | 1844 | ||
1770 | mwl8k_tx_start(priv); | 1845 | mwl8k_tx_start(priv); |
1771 | 1846 | ||
1772 | spin_unlock_bh(&priv->tx_lock); | 1847 | spin_unlock_bh(&priv->tx_lock); |
1848 | |||
1849 | /* Initiate the ampdu session here */ | ||
1850 | if (start_ba_session) { | ||
1851 | spin_lock(&priv->stream_lock); | ||
1852 | if (mwl8k_start_stream(hw, stream)) | ||
1853 | mwl8k_remove_stream(hw, stream); | ||
1854 | spin_unlock(&priv->stream_lock); | ||
1855 | } | ||
1773 | } | 1856 | } |
1774 | 1857 | ||
1775 | 1858 | ||
@@ -4632,21 +4715,118 @@ static int mwl8k_get_survey(struct ieee80211_hw *hw, int idx, | |||
4632 | return 0; | 4715 | return 0; |
4633 | } | 4716 | } |
4634 | 4717 | ||
4718 | #define MAX_AMPDU_ATTEMPTS 5 | ||
4719 | |||
4635 | static int | 4720 | static int |
4636 | mwl8k_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, | 4721 | mwl8k_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, |
4637 | enum ieee80211_ampdu_mlme_action action, | 4722 | enum ieee80211_ampdu_mlme_action action, |
4638 | struct ieee80211_sta *sta, u16 tid, u16 *ssn, | 4723 | struct ieee80211_sta *sta, u16 tid, u16 *ssn, |
4639 | u8 buf_size) | 4724 | u8 buf_size) |
4640 | { | 4725 | { |
4726 | |||
4727 | int i, rc = 0; | ||
4728 | struct mwl8k_priv *priv = hw->priv; | ||
4729 | struct mwl8k_ampdu_stream *stream; | ||
4730 | u8 *addr = sta->addr; | ||
4731 | |||
4732 | if (!(hw->flags & IEEE80211_HW_AMPDU_AGGREGATION)) | ||
4733 | return -ENOTSUPP; | ||
4734 | |||
4735 | spin_lock(&priv->stream_lock); | ||
4736 | stream = mwl8k_lookup_stream(hw, addr, tid); | ||
4737 | |||
4641 | switch (action) { | 4738 | switch (action) { |
4642 | case IEEE80211_AMPDU_RX_START: | 4739 | case IEEE80211_AMPDU_RX_START: |
4643 | case IEEE80211_AMPDU_RX_STOP: | 4740 | case IEEE80211_AMPDU_RX_STOP: |
4644 | if (!(hw->flags & IEEE80211_HW_AMPDU_AGGREGATION)) | 4741 | break; |
4645 | return -ENOTSUPP; | 4742 | case IEEE80211_AMPDU_TX_START: |
4646 | return 0; | 4743 | /* By the time we get here the hw queues may contain outgoing |
4744 | * packets for this RA/TID that are not part of this BA | ||
4745 | * session. The hw will assign sequence numbers to these | ||
4746 | * packets as they go out. So if we query the hw for its next | ||
4747 | * sequence number and use that for the SSN here, it may end up | ||
4748 | * being wrong, which will lead to sequence number mismatch at | ||
4749 | * the recipient. To avoid this, we reset the sequence number | ||
4750 | * to O for the first MPDU in this BA stream. | ||
4751 | */ | ||
4752 | *ssn = 0; | ||
4753 | if (stream == NULL) { | ||
4754 | /* This means that somebody outside this driver called | ||
4755 | * ieee80211_start_tx_ba_session. This is unexpected | ||
4756 | * because we do our own rate control. Just warn and | ||
4757 | * move on. | ||
4758 | */ | ||
4759 | wiphy_warn(hw->wiphy, "Unexpected call to %s. " | ||
4760 | "Proceeding anyway.\n", __func__); | ||
4761 | stream = mwl8k_add_stream(hw, sta, tid); | ||
4762 | } | ||
4763 | if (stream == NULL) { | ||
4764 | wiphy_debug(hw->wiphy, "no free AMPDU streams\n"); | ||
4765 | rc = -EBUSY; | ||
4766 | break; | ||
4767 | } | ||
4768 | stream->state = AMPDU_STREAM_IN_PROGRESS; | ||
4769 | |||
4770 | /* Release the lock before we do the time consuming stuff */ | ||
4771 | spin_unlock(&priv->stream_lock); | ||
4772 | for (i = 0; i < MAX_AMPDU_ATTEMPTS; i++) { | ||
4773 | rc = mwl8k_check_ba(hw, stream); | ||
4774 | |||
4775 | if (!rc) | ||
4776 | break; | ||
4777 | /* | ||
4778 | * HW queues take time to be flushed, give them | ||
4779 | * sufficient time | ||
4780 | */ | ||
4781 | |||
4782 | msleep(1000); | ||
4783 | } | ||
4784 | spin_lock(&priv->stream_lock); | ||
4785 | if (rc) { | ||
4786 | wiphy_err(hw->wiphy, "Stream for tid %d busy after %d" | ||
4787 | " attempts\n", tid, MAX_AMPDU_ATTEMPTS); | ||
4788 | mwl8k_remove_stream(hw, stream); | ||
4789 | rc = -EBUSY; | ||
4790 | break; | ||
4791 | } | ||
4792 | ieee80211_start_tx_ba_cb_irqsafe(vif, addr, tid); | ||
4793 | break; | ||
4794 | case IEEE80211_AMPDU_TX_STOP: | ||
4795 | if (stream == NULL) | ||
4796 | break; | ||
4797 | if (stream->state == AMPDU_STREAM_ACTIVE) { | ||
4798 | spin_unlock(&priv->stream_lock); | ||
4799 | mwl8k_destroy_ba(hw, stream); | ||
4800 | spin_lock(&priv->stream_lock); | ||
4801 | } | ||
4802 | mwl8k_remove_stream(hw, stream); | ||
4803 | ieee80211_stop_tx_ba_cb_irqsafe(vif, addr, tid); | ||
4804 | break; | ||
4805 | case IEEE80211_AMPDU_TX_OPERATIONAL: | ||
4806 | BUG_ON(stream == NULL); | ||
4807 | BUG_ON(stream->state != AMPDU_STREAM_IN_PROGRESS); | ||
4808 | spin_unlock(&priv->stream_lock); | ||
4809 | rc = mwl8k_create_ba(hw, stream, buf_size); | ||
4810 | spin_lock(&priv->stream_lock); | ||
4811 | if (!rc) | ||
4812 | stream->state = AMPDU_STREAM_ACTIVE; | ||
4813 | else { | ||
4814 | spin_unlock(&priv->stream_lock); | ||
4815 | mwl8k_destroy_ba(hw, stream); | ||
4816 | spin_lock(&priv->stream_lock); | ||
4817 | wiphy_debug(hw->wiphy, | ||
4818 | "Failed adding stream for sta %pM tid %d\n", | ||
4819 | addr, tid); | ||
4820 | mwl8k_remove_stream(hw, stream); | ||
4821 | } | ||
4822 | break; | ||
4823 | |||
4647 | default: | 4824 | default: |
4648 | return -ENOTSUPP; | 4825 | rc = -ENOTSUPP; |
4649 | } | 4826 | } |
4827 | |||
4828 | spin_unlock(&priv->stream_lock); | ||
4829 | return rc; | ||
4650 | } | 4830 | } |
4651 | 4831 | ||
4652 | static const struct ieee80211_ops mwl8k_ops = { | 4832 | static const struct ieee80211_ops mwl8k_ops = { |