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 /drivers/net | |
| 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>
Diffstat (limited to 'drivers/net')
| -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 dcd4508b1fd4..ec1190ab0f8e 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 = { |
