|
@@ -137,6 +137,13 @@ struct mwl8k_tx_queue {
|
|
|
struct sk_buff **skb;
|
|
|
};
|
|
|
|
|
|
+enum {
|
|
|
+ AMPDU_NO_STREAM,
|
|
|
+ AMPDU_STREAM_NEW,
|
|
|
+ AMPDU_STREAM_IN_PROGRESS,
|
|
|
+ AMPDU_STREAM_ACTIVE,
|
|
|
+};
|
|
|
+
|
|
|
struct mwl8k_ampdu_stream {
|
|
|
struct ieee80211_sta *sta;
|
|
|
u8 tid;
|
|
@@ -172,6 +179,8 @@ struct mwl8k_priv {
|
|
|
|
|
|
/* Ampdu stream information */
|
|
|
u8 num_ampdu_queues;
|
|
|
+ spinlock_t stream_lock;
|
|
|
+ struct mwl8k_ampdu_stream ampdu[MWL8K_MAX_AMPDU_QUEUES];
|
|
|
|
|
|
/* firmware access */
|
|
|
struct mutex fw_mutex;
|
|
@@ -1594,6 +1603,74 @@ static void mwl8k_txq_deinit(struct ieee80211_hw *hw, int index)
|
|
|
txq->txd = NULL;
|
|
|
}
|
|
|
|
|
|
+/* caller must hold priv->stream_lock when calling the stream functions */
|
|
|
+struct mwl8k_ampdu_stream *
|
|
|
+mwl8k_add_stream(struct ieee80211_hw *hw, struct ieee80211_sta *sta, u8 tid)
|
|
|
+{
|
|
|
+ struct mwl8k_ampdu_stream *stream;
|
|
|
+ struct mwl8k_priv *priv = hw->priv;
|
|
|
+ int i;
|
|
|
+
|
|
|
+ for (i = 0; i < priv->num_ampdu_queues; i++) {
|
|
|
+ stream = &priv->ampdu[i];
|
|
|
+ if (stream->state == AMPDU_NO_STREAM) {
|
|
|
+ stream->sta = sta;
|
|
|
+ stream->state = AMPDU_STREAM_NEW;
|
|
|
+ stream->tid = tid;
|
|
|
+ stream->idx = i;
|
|
|
+ stream->txq_idx = MWL8K_TX_WMM_QUEUES + i;
|
|
|
+ wiphy_debug(hw->wiphy, "Added a new stream for %pM %d",
|
|
|
+ sta->addr, tid);
|
|
|
+ return stream;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return NULL;
|
|
|
+}
|
|
|
+
|
|
|
+static int
|
|
|
+mwl8k_start_stream(struct ieee80211_hw *hw, struct mwl8k_ampdu_stream *stream)
|
|
|
+{
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ /* if the stream has already been started, don't start it again */
|
|
|
+ if (stream->state != AMPDU_STREAM_NEW)
|
|
|
+ return 0;
|
|
|
+ ret = ieee80211_start_tx_ba_session(stream->sta, stream->tid, 0);
|
|
|
+ if (ret)
|
|
|
+ wiphy_debug(hw->wiphy, "Failed to start stream for %pM %d: "
|
|
|
+ "%d\n", stream->sta->addr, stream->tid, ret);
|
|
|
+ else
|
|
|
+ wiphy_debug(hw->wiphy, "Started stream for %pM %d\n",
|
|
|
+ stream->sta->addr, stream->tid);
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+static void
|
|
|
+mwl8k_remove_stream(struct ieee80211_hw *hw, struct mwl8k_ampdu_stream *stream)
|
|
|
+{
|
|
|
+ wiphy_debug(hw->wiphy, "Remove stream for %pM %d\n", stream->sta->addr,
|
|
|
+ stream->tid);
|
|
|
+ memset(stream, 0, sizeof(*stream));
|
|
|
+}
|
|
|
+
|
|
|
+static struct mwl8k_ampdu_stream *
|
|
|
+mwl8k_lookup_stream(struct ieee80211_hw *hw, u8 *addr, u8 tid)
|
|
|
+{
|
|
|
+ struct mwl8k_priv *priv = hw->priv;
|
|
|
+ int i;
|
|
|
+
|
|
|
+ for (i = 0 ; i < priv->num_ampdu_queues; i++) {
|
|
|
+ struct mwl8k_ampdu_stream *stream;
|
|
|
+ stream = &priv->ampdu[i];
|
|
|
+ if (stream->state == AMPDU_NO_STREAM)
|
|
|
+ continue;
|
|
|
+ if (!memcmp(stream->sta->addr, addr, ETH_ALEN) &&
|
|
|
+ stream->tid == tid)
|
|
|
+ return stream;
|
|
|
+ }
|
|
|
+ return NULL;
|
|
|
+}
|
|
|
+
|
|
|
static void
|
|
|
mwl8k_txq_xmit(struct ieee80211_hw *hw, int index, struct sk_buff *skb)
|
|
|
{
|
|
@@ -4854,6 +4931,8 @@ static int mwl8k_probe_hw(struct ieee80211_hw *hw)
|
|
|
goto err_free_queues;
|
|
|
}
|
|
|
|
|
|
+ memset(priv->ampdu, 0, sizeof(priv->ampdu));
|
|
|
+
|
|
|
/*
|
|
|
* Temporarily enable interrupts. Initial firmware host
|
|
|
* commands use interrupts and avoid polling. Disable
|
|
@@ -5018,6 +5097,8 @@ static int mwl8k_firmware_load_success(struct mwl8k_priv *priv)
|
|
|
|
|
|
spin_lock_init(&priv->tx_lock);
|
|
|
|
|
|
+ spin_lock_init(&priv->stream_lock);
|
|
|
+
|
|
|
priv->tx_wait = NULL;
|
|
|
|
|
|
rc = mwl8k_probe_hw(hw);
|