2008-01-28 12:08:49

by Ron Rindjunsky

[permalink] [raw]
Subject: [PATCH 0/12 v2] mac80211/iwlwifi: A-MPDU Tx support

This series of patches contains implementation for the IEEE802.11n
aggregated MPDUs (A-MPDU) MLME. The series handles Tx A-MPDU (initiator party).

Please notice:
===============
As patches "cfg80211 API for channels/bitrates, mac80211 and driver conversion"
and "nl80211: export hardware bitrate/channel capabilities" are still unstable,
and cause current code to stop function, this series does not use those patches.

Notes to this series:
======================
This series of patches splits into:
- patches 1-8 add 802.11n Tx A-MPDU MLME support to mac80211.
- patches 9-12 demonstrates the use of the above mac80211's MLME through iwl4965 low level driver

The patches in the series should not be treated as stand alone patches,
but as a complete MLME according to IEEE802.11n spec, although separation
to patches by subjects was made for ease of handling.

The patches do _not_ break any existing driver.
Patches were made (and tested to work) with wireless branch 2.6.24-rc8 (#everything),
on top of commit 597d23b88aca15dc4be31f051e06a949fd1a497a.

The MLME framework includes the following:
- A-MPDU MLME (for aggregated Tx per STA/TID)
- addBA request support.
- delBA request support (initiator party).
- qdisc support by STA/TID.
- Tx rate scaling support (block Ack aware)




2008-01-28 12:07:38

by Ron Rindjunsky

[permalink] [raw]
Subject: [PATCH 04/12 v4] mac80211: A-MPDU Tx adding qdisc support

This patch allows qdisc support in A-MPDU Tx. a method to
handle QoS <-> TID switches is present in this patch.

Signed-off-by: Ron Rindjunsky <[email protected]>
---
include/net/mac80211.h | 8 ++-
net/mac80211/ieee80211.c | 8 +-
net/mac80211/ieee80211_i.h | 5 +-
net/mac80211/tx.c | 2 +
net/mac80211/wme.c | 135 ++++++++++++++++++++++++++++++++++++++++---
net/mac80211/wme.h | 23 +++++++-
6 files changed, 161 insertions(+), 20 deletions(-)

Index: wl2_6_24_rc8_ev/include/net/mac80211.h
===================================================================
--- wl2_6_24_rc8_ev.orig/include/net/mac80211.h
+++ wl2_6_24_rc8_ev/include/net/mac80211.h
@@ -246,6 +246,7 @@ struct ieee80211_tx_queue_stats_data {
* @IEEE80211_TX_QUEUE_AFTER_BEACON: transmit queue for frames to be
* sent after a beacon
* @IEEE80211_TX_QUEUE_BEACON: transmit queue for beacon frames
+ * @NUM_TX_DATA_QUEUES_AMPDU: adding more queues for A-MPDU
*/
enum ieee80211_tx_queue {
IEEE80211_TX_QUEUE_DATA0,
@@ -261,11 +262,12 @@ enum ieee80211_tx_queue {
* this struct need to have fixed values. As soon as it is removed, we can
* fix these entries. */
IEEE80211_TX_QUEUE_AFTER_BEACON = 6,
- IEEE80211_TX_QUEUE_BEACON = 7
+ IEEE80211_TX_QUEUE_BEACON = 7,
+ NUM_TX_DATA_QUEUES_AMPDU = 16
};

struct ieee80211_tx_queue_stats {
- struct ieee80211_tx_queue_stats_data data[NUM_TX_DATA_QUEUES];
+ struct ieee80211_tx_queue_stats_data data[NUM_TX_DATA_QUEUES_AMPDU];
};

struct ieee80211_low_level_stats {
@@ -348,6 +350,8 @@ struct ieee80211_tx_control {
#define IEEE80211_TXCTL_EAPOL_FRAME (1<<11) /* internal to mac80211 */
#define IEEE80211_TXCTL_SEND_AFTER_DTIM (1<<12) /* send this frame after DTIM
* beacon */
+#define IEEE80211_TXCTL_AMPDU (1<<13) /* this frame should be sent
+ * as part of an A-MPDU */
u32 flags; /* tx control flags defined
* above */
u8 key_idx; /* keyidx from hw->set_key(), undefined if
Index: wl2_6_24_rc8_ev/net/mac80211/ieee80211.c
===================================================================
--- wl2_6_24_rc8_ev.orig/net/mac80211/ieee80211.c
+++ wl2_6_24_rc8_ev/net/mac80211/ieee80211.c
@@ -459,7 +459,7 @@ int ieee80211_start_tx_ba_session(struct
spin_lock_bh(&local->mdev->queue_lock);

/* create a new queue for this aggregation */
- /* ret = ieee80211_ht_agg_queue_add(local, sta, tid); */
+ ret = ieee80211_ht_agg_queue_add(local, sta, tid);

/* case no queue is available to aggregation
* don't switch to aggregation */
@@ -485,7 +485,7 @@ int ieee80211_start_tx_ba_session(struct
/* No need to requeue the packets in the agg queue, since we
* held the tx lock: no packet could be enqueued to the newly
* allocated queue */
- /* ieee80211_ht_agg_queue_remove(local, sta, tid, 0); */
+ ieee80211_ht_agg_queue_remove(local, sta, tid, 0);
#ifdef CONFIG_MAC80211_HT_DEBUG
printk(KERN_DEBUG "BA request denied - HW or queue unavailable"
" for tid %d\n", tid);
@@ -496,7 +496,7 @@ int ieee80211_start_tx_ba_session(struct
}

/* Will put all the packets in the new SW queue */
- /* ieee80211_requeue(local, ieee802_1d_to_ac[tid]); */
+ ieee80211_requeue(local, ieee802_1d_to_ac[tid]);
spin_unlock_bh(&local->mdev->queue_lock);

/* We have most probably almost emptied the legacy queue */
@@ -672,7 +672,7 @@ void ieee80211_stop_tx_ba_cb(struct ieee
* the content of the qdiscs */
spin_lock_bh(&local->mdev->queue_lock);
/* remove the queue for this aggregation */
- /* ieee80211_ht_agg_queue_remove(local, sta, tid, 1); */
+ ieee80211_ht_agg_queue_remove(local, sta, tid, 1);
spin_unlock_bh(&local->mdev->queue_lock);

/* we just requeued the all the frames that were in the removed
Index: wl2_6_24_rc8_ev/net/mac80211/ieee80211_i.h
===================================================================
--- wl2_6_24_rc8_ev.orig/net/mac80211/ieee80211_i.h
+++ wl2_6_24_rc8_ev/net/mac80211/ieee80211_i.h
@@ -165,6 +165,7 @@ struct ieee80211_txrx_data {
#define IEEE80211_TXPD_DO_NOT_ENCRYPT BIT(1)
#define IEEE80211_TXPD_REQUEUE BIT(2)
#define IEEE80211_TXPD_EAPOL_FRAME BIT(3)
+#define IEEE80211_TXPD_AMPDU BIT(4)
/* Stored in sk_buff->cb */
struct ieee80211_tx_packet_data {
int ifindex;
@@ -452,8 +453,8 @@ struct ieee80211_local {
struct sta_info *sta_hash[STA_HASH_SIZE];
struct timer_list sta_cleanup;

- unsigned long state[NUM_TX_DATA_QUEUES];
- struct ieee80211_tx_stored_packet pending_packet[NUM_TX_DATA_QUEUES];
+ unsigned long state[NUM_TX_DATA_QUEUES_AMPDU];
+ struct ieee80211_tx_stored_packet pending_packet[NUM_TX_DATA_QUEUES_AMPDU];
struct tasklet_struct tx_pending_tasklet;

/* number of interfaces with corresponding IFF_ flags */
Index: wl2_6_24_rc8_ev/net/mac80211/tx.c
===================================================================
--- wl2_6_24_rc8_ev.orig/net/mac80211/tx.c
+++ wl2_6_24_rc8_ev/net/mac80211/tx.c
@@ -1260,6 +1260,8 @@ int ieee80211_master_start_xmit(struct s
control.flags |= IEEE80211_TXCTL_REQUEUE;
if (pkt_data->flags & IEEE80211_TXPD_EAPOL_FRAME)
control.flags |= IEEE80211_TXCTL_EAPOL_FRAME;
+ if (pkt_data->flags & IEEE80211_TXPD_AMPDU)
+ control.flags |= IEEE80211_TXCTL_AMPDU;
control.queue = pkt_data->queue;

ret = ieee80211_tx(odev, skb, &control);
Index: wl2_6_24_rc8_ev/net/mac80211/wme.c
===================================================================
--- wl2_6_24_rc8_ev.orig/net/mac80211/wme.c
+++ wl2_6_24_rc8_ev/net/mac80211/wme.c
@@ -19,10 +19,13 @@
#include "wme.h"

/* maximum number of hardware queues we support. */
-#define TC_80211_MAX_QUEUES 8
+#define TC_80211_MAX_QUEUES 16
+
+const int ieee802_1d_to_ac[8] = { 2, 3, 3, 2, 1, 1, 0, 0 };

struct ieee80211_sched_data
{
+ unsigned long qdisc_pool;
struct tcf_proto *filter_list;
struct Qdisc *queues[TC_80211_MAX_QUEUES];
struct sk_buff_head requeued[TC_80211_MAX_QUEUES];
@@ -98,7 +101,6 @@ static inline int classify80211(struct s
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
unsigned short fc = le16_to_cpu(hdr->frame_control);
int qos;
- const int ieee802_1d_to_ac[8] = { 2, 3, 3, 2, 1, 1, 0, 0 };

/* see if frame is data or non data frame */
if (unlikely((fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA)) {
@@ -146,9 +148,25 @@ static int wme_qdiscop_enqueue(struct sk
unsigned short fc = le16_to_cpu(hdr->frame_control);
struct Qdisc *qdisc;
int err, queue;
+ struct sta_info *sta;
+ u8 tid;

if (pkt_data->flags & IEEE80211_TXPD_REQUEUE) {
- skb_queue_tail(&q->requeued[pkt_data->queue], skb);
+ queue = pkt_data->queue;
+ sta = sta_info_get(local, hdr->addr1);
+ tid = skb->priority & QOS_CONTROL_TAG1D_MASK;
+ if (sta) {
+ int ampdu_queue = sta->tid_to_tx_q[tid];
+ if ((ampdu_queue < local->hw.queues) &&
+ test_bit(ampdu_queue, &q->qdisc_pool)) {
+ queue = ampdu_queue;
+ pkt_data->flags |= IEEE80211_TXPD_AMPDU;
+ } else {
+ pkt_data->flags &= ~IEEE80211_TXPD_AMPDU;
+ }
+ sta_info_put(sta);
+ }
+ skb_queue_tail(&q->requeued[queue], skb);
qd->q.qlen++;
return 0;
}
@@ -159,14 +177,28 @@ static int wme_qdiscop_enqueue(struct sk
*/
if (WLAN_FC_IS_QOS_DATA(fc)) {
u8 *p = skb->data + ieee80211_get_hdrlen(fc) - 2;
- u8 qos_hdr = skb->priority & QOS_CONTROL_TAG1D_MASK;
+ u8 ack_policy = 0;
+ tid = skb->priority & QOS_CONTROL_TAG1D_MASK;
if (local->wifi_wme_noack_test)
- qos_hdr |= QOS_CONTROL_ACK_POLICY_NOACK <<
+ ack_policy |= QOS_CONTROL_ACK_POLICY_NOACK <<
QOS_CONTROL_ACK_POLICY_SHIFT;
/* qos header is 2 bytes, second reserved */
- *p = qos_hdr;
+ *p = ack_policy | tid;
p++;
*p = 0;
+
+ sta = sta_info_get(local, hdr->addr1);
+ if (sta) {
+ int ampdu_queue = sta->tid_to_tx_q[tid];
+ if ((ampdu_queue < local->hw.queues) &&
+ test_bit(ampdu_queue, &q->qdisc_pool)) {
+ queue = ampdu_queue;
+ pkt_data->flags |= IEEE80211_TXPD_AMPDU;
+ } else {
+ pkt_data->flags &= ~IEEE80211_TXPD_AMPDU;
+ }
+ sta_info_put(sta);
+ }
}

if (unlikely(queue >= local->hw.queues)) {
@@ -184,6 +216,7 @@ static int wme_qdiscop_enqueue(struct sk
kfree_skb(skb);
err = NET_XMIT_DROP;
} else {
+ tid = skb->priority & QOS_CONTROL_TAG1D_MASK;
pkt_data->queue = (unsigned int) queue;
qdisc = q->queues[queue];
err = qdisc->enqueue(skb, qdisc);
@@ -235,10 +268,11 @@ static struct sk_buff *wme_qdiscop_deque
/* check all the h/w queues in numeric/priority order */
for (queue = 0; queue < hw->queues; queue++) {
/* see if there is room in this hardware queue */
- if (test_bit(IEEE80211_LINK_STATE_XOFF,
- &local->state[queue]) ||
- test_bit(IEEE80211_LINK_STATE_PENDING,
- &local->state[queue]))
+ if ((test_bit(IEEE80211_LINK_STATE_XOFF,
+ &local->state[queue])) ||
+ (test_bit(IEEE80211_LINK_STATE_PENDING,
+ &local->state[queue])) ||
+ (!test_bit(queue, &q->qdisc_pool)))
continue;

/* there is space - try and get a frame */
@@ -360,6 +394,10 @@ static int wme_qdiscop_init(struct Qdisc
}
}

+ /* reserve all legacy QoS queues */
+ for (i = 0; i < min(IEEE80211_TX_QUEUE_DATA4, queues); i++)
+ set_bit(i, &q->qdisc_pool);
+
return err;
}

@@ -605,3 +643,80 @@ void ieee80211_wme_unregister(void)
{
unregister_qdisc(&wme_qdisc_ops);
}
+
+int ieee80211_ht_agg_queue_add(struct ieee80211_local *local,
+ struct sta_info *sta, u16 tid)
+{
+ int i;
+ struct ieee80211_sched_data *q =
+ qdisc_priv(local->mdev->qdisc_sleeping);
+ DECLARE_MAC_BUF(mac);
+
+ /* prepare the filter and save it for the SW queue
+ * matching the recieved HW queue */
+
+ /* try to get a Qdisc from the pool */
+ for (i = IEEE80211_TX_QUEUE_BEACON; i < local->hw.queues; i++)
+ if (!test_and_set_bit(i, &q->qdisc_pool)) {
+ ieee80211_stop_queue(local_to_hw(local), i);
+ sta->tid_to_tx_q[tid] = i;
+
+ /* IF there are already pending packets
+ * on this tid first we need to drain them
+ * on the previous queue
+ * since HT is strict in order */
+#ifdef CONFIG_MAC80211_HT_DEBUG
+ if (net_ratelimit())
+ printk(KERN_DEBUG "allocated aggregation queue"
+ " %d tid %d addr %s pool=0x%lX\n",
+ i, tid, print_mac(mac, sta->addr),
+ q->qdisc_pool);
+#endif /* CONFIG_MAC80211_HT_DEBUG */
+ return 0;
+ }
+
+ return -EAGAIN;
+}
+
+/**
+ * the caller needs to hold local->mdev->queue_lock
+ */
+void ieee80211_ht_agg_queue_remove(struct ieee80211_local *local,
+ struct sta_info *sta, u16 tid,
+ u8 requeue)
+{
+ struct ieee80211_sched_data *q =
+ qdisc_priv(local->mdev->qdisc_sleeping);
+ int agg_queue = sta->tid_to_tx_q[tid];
+
+ /* return the qdisc to the pool */
+ clear_bit(agg_queue, &q->qdisc_pool);
+ sta->tid_to_tx_q[tid] = local->hw.queues;
+
+ if (requeue)
+ ieee80211_requeue(local, agg_queue);
+ else
+ q->queues[agg_queue]->ops->reset(q->queues[agg_queue]);
+}
+
+void ieee80211_requeue(struct ieee80211_local *local, int queue)
+{
+ struct Qdisc *root_qd = local->mdev->qdisc_sleeping;
+ struct ieee80211_sched_data *q = qdisc_priv(root_qd);
+ struct Qdisc *qdisc = q->queues[queue];
+ struct sk_buff *skb = NULL;
+ u32 len = qdisc->q.qlen;
+
+ if (!qdisc || !qdisc->dequeue)
+ return;
+
+ printk(KERN_DEBUG "requeue: qlen = %d\n", qdisc->q.qlen);
+ for (len = qdisc->q.qlen; len > 0; len--) {
+ skb = qdisc->dequeue(qdisc);
+ root_qd->q.qlen--;
+ /* packet will be classified again and */
+ /* skb->packet_data->queue will be overridden if needed */
+ if (skb)
+ wme_qdiscop_enqueue(skb, root_qd);
+ }
+}
Index: wl2_6_24_rc8_ev/net/mac80211/wme.h
===================================================================
--- wl2_6_24_rc8_ev.orig/net/mac80211/wme.h
+++ wl2_6_24_rc8_ev/net/mac80211/wme.h
@@ -24,6 +24,8 @@

#define QOS_CONTROL_TAG1D_MASK 0x07

+extern const int ieee802_1d_to_ac[8];
+
static inline int WLAN_FC_IS_QOS_DATA(u16 fc)
{
return (fc & 0x8C) == 0x88;
@@ -32,7 +34,12 @@ static inline int WLAN_FC_IS_QOS_DATA(u1
#ifdef CONFIG_NET_SCHED
void ieee80211_install_qdisc(struct net_device *dev);
int ieee80211_qdisc_installed(struct net_device *dev);
-
+int ieee80211_ht_agg_queue_add(struct ieee80211_local *local,
+ struct sta_info *sta, u16 tid);
+void ieee80211_ht_agg_queue_remove(struct ieee80211_local *local,
+ struct sta_info *sta, u16 tid,
+ u8 requeue);
+void ieee80211_requeue(struct ieee80211_local *local, int queue);
int ieee80211_wme_register(void);
void ieee80211_wme_unregister(void);
#else
@@ -43,7 +50,19 @@ static inline int ieee80211_qdisc_instal
{
return 0;
}
-
+static inline int ieee80211_ht_agg_queue_add(struct ieee80211_local *local,
+ struct sta_info *sta, u16 tid)
+{
+ return -EAGAIN;
+}
+static inline void ieee80211_ht_agg_queue_remove(struct ieee80211_local *local,
+ struct sta_info *sta, u16 tid,
+ u8 requeue)
+{
+}
+static inline void ieee80211_requeue(struct ieee80211_local *local, int queue)
+{
+}
static inline int ieee80211_wme_register(void)
{
return 0;

2008-01-28 12:07:37

by Ron Rindjunsky

[permalink] [raw]
Subject: [PATCH 06/12 v2] mac80211: A-MPDU add debugfs support

This patch adds A-MPDU status report per STA to the debugfs.
The option to de/activate A-MPDU through debugfs is also present.

Signed-off-by: Ron Rindjunsky <[email protected]>
---
net/mac80211/debugfs_sta.c | 116 ++++++++++++++++++++++++++++++++++++++++++++
net/mac80211/sta_info.h | 1 +
2 files changed, 117 insertions(+), 0 deletions(-)

diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c
index 8f5944c..df25abf 100644
--- a/net/mac80211/debugfs_sta.c
+++ b/net/mac80211/debugfs_sta.c
@@ -55,6 +55,13 @@ static const struct file_operations sta_ ##name## _ops = { \
.open = mac80211_open_file_generic, \
}

+#define STA_OPS_WR(name) \
+static const struct file_operations sta_ ##name## _ops = { \
+ .read = sta_##name##_read, \
+ .write = sta_##name##_write, \
+ .open = mac80211_open_file_generic, \
+}
+
#define STA_FILE(name, field, format) \
STA_READ_##format(name, field) \
STA_OPS(name)
@@ -191,6 +198,113 @@ static ssize_t sta_wme_tx_queue_read(struct file *file, char __user *userbuf,
STA_OPS(wme_tx_queue);
#endif

+static ssize_t sta_agg_status_read(struct file *file, char __user *userbuf,
+ size_t count, loff_t *ppos)
+{
+ char buf[768], *p = buf;
+ int i;
+ struct sta_info *sta = file->private_data;
+ p += scnprintf(p, sizeof(buf)+buf-p, "Agg state for STA is:\n");
+ p += scnprintf(p, sizeof(buf)+buf-p, " STA next dialog_token is %d \n "
+ "TIDs info is: \n TID :",
+ (sta->ampdu_mlme.dialog_token_allocator + 1));
+ for (i = 0; i < STA_TID_NUM; i++)
+ p += scnprintf(p, sizeof(buf)+buf-p, "%5d", i);
+
+ p += scnprintf(p, sizeof(buf)+buf-p, "\n RX :");
+ for (i = 0; i < STA_TID_NUM; i++)
+ p += scnprintf(p, sizeof(buf)+buf-p, "%5d",
+ sta->ampdu_mlme.tid_rx[i].state);
+
+ p += scnprintf(p, sizeof(buf)+buf-p, "\n DTKN:");
+ for (i = 0; i < STA_TID_NUM; i++)
+ p += scnprintf(p, sizeof(buf)+buf-p, "%5d",
+ sta->ampdu_mlme.tid_rx[i].dialog_token);
+
+ p += scnprintf(p, sizeof(buf)+buf-p, "\n TX :");
+ for (i = 0; i < STA_TID_NUM; i++)
+ p += scnprintf(p, sizeof(buf)+buf-p, "%5d",
+ sta->ampdu_mlme.tid_tx[i].state);
+
+ p += scnprintf(p, sizeof(buf)+buf-p, "\n DTKN:");
+ for (i = 0; i < STA_TID_NUM; i++)
+ p += scnprintf(p, sizeof(buf)+buf-p, "%5d",
+ sta->ampdu_mlme.tid_tx[i].dialog_token);
+
+ p += scnprintf(p, sizeof(buf)+buf-p, "\n SSN :");
+ for (i = 0; i < STA_TID_NUM; i++)
+ p += scnprintf(p, sizeof(buf)+buf-p, "%5d",
+ sta->ampdu_mlme.tid_tx[i].ssn);
+
+ p += scnprintf(p, sizeof(buf)+buf-p, "\n");
+
+ return simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);
+}
+
+static ssize_t sta_agg_status_write(struct file *file,
+ const char __user *user_buf, size_t count, loff_t *ppos)
+{
+ struct sta_info *sta = file->private_data;
+ struct net_device *dev = sta->dev;
+ struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+ struct ieee80211_hw *hw = &local->hw;
+ u8 *da = sta->addr;
+ static int tid_static_tx[16] = {0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0};
+ static int tid_static_rx[16] = {1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1};
+ char *endp;
+ char buf[32];
+ int buf_size, rs;
+ unsigned int tid_num;
+ char state[4];
+
+ memset(buf, 0x00, sizeof(buf));
+ buf_size = min(count, (sizeof(buf)-1));
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+
+ tid_num = simple_strtoul(buf, &endp, 0);
+ if (endp == buf)
+ return -EINVAL;
+
+ if ((tid_num >= 100) && (tid_num <= 115)) {
+ /* toggle Rx aggregation command */
+ tid_num = tid_num - 100;
+ if (tid_static_rx[tid_num] == 1) {
+ strcpy(state, "off ");
+ ieee80211_sta_stop_rx_ba_session(dev, da, tid_num, 0,
+ WLAN_REASON_QSTA_REQUIRE_SETUP);
+ sta->ampdu_mlme.tid_rx[tid_num].buf_size = 0xFF;
+ tid_static_rx[tid_num] = 0;
+ } else {
+ strcpy(state, "on ");
+ sta->ampdu_mlme.tid_rx[tid_num].buf_size = 0x00;
+ tid_static_rx[tid_num] = 1;
+ }
+ printk(KERN_DEBUG "debugfs - try switching tid %u %s\n",
+ tid_num, state);
+ } else if ((tid_num >= 0) && (tid_num <= 15)) {
+ /* toggle Tx aggregation command */
+ if (tid_static_tx[tid_num] == 0) {
+ strcpy(state, "on ");
+ rs = ieee80211_start_tx_ba_session(hw, da, tid_num);
+ if (rs == 0)
+ tid_static_tx[tid_num] = 1;
+ } else {
+ strcpy(state, "off");
+ rs = ieee80211_stop_tx_ba_session(hw, da, tid_num, 1);
+ if (rs == 0)
+ tid_static_tx[tid_num] = 0;
+ }
+ printk(KERN_DEBUG "debugfs - switching tid %u %s, return=%d\n",
+ tid_num, state, rs);
+ }
+
+ return count;
+}
+STA_OPS_WR(agg_status);
+
#define DEBUGFS_ADD(name) \
sta->debugfs.name = debugfs_create_file(#name, 0444, \
sta->debugfs.dir, sta, &sta_ ##name## _ops);
@@ -224,6 +338,7 @@ void ieee80211_sta_debugfs_add(struct sta_info *sta)
DEBUGFS_ADD(wme_rx_queue);
DEBUGFS_ADD(wme_tx_queue);
#endif
+ DEBUGFS_ADD(agg_status);
}

void ieee80211_sta_debugfs_remove(struct sta_info *sta)
@@ -238,6 +353,7 @@ void ieee80211_sta_debugfs_remove(struct sta_info *sta)
DEBUGFS_DEL(wme_rx_queue);
DEBUGFS_DEL(wme_tx_queue);
#endif
+ DEBUGFS_DEL(agg_status);

debugfs_remove(sta->debugfs.dir);
sta->debugfs.dir = NULL;
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index 48a620a..75573dc 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -192,6 +192,7 @@ struct sta_info {
struct dentry *wme_rx_queue;
struct dentry *wme_tx_queue;
#endif
+ struct dentry *agg_status;
} debugfs;
#endif
};
--
1.5.3.3


2008-01-28 12:07:39

by Ron Rindjunsky

[permalink] [raw]
Subject: [PATCH 03/12 v3] mac80211: A-MPDU Tx adding basic functionality

This patch adds the following abilities to mac80211:
- start A-MPDU Tx session
- stop A-MPDU Tx session
- call backs to start/stop A-MPDU Tx session
- sending addBA request
- processing addBA response

Signed-off-by: Ron Rindjunsky <[email protected]>
---
net/mac80211/ieee80211.c | 336 ++++++++++++++++++++++++++++++++++++++++++
net/mac80211/ieee80211_i.h | 14 ++
net/mac80211/ieee80211_sta.c | 178 ++++++++++++++++++++++
3 files changed, 528 insertions(+), 0 deletions(-)

Index: wl2_6_24_rc8_ev/net/mac80211/ieee80211.c
===================================================================
--- wl2_6_24_rc8_ev.orig/net/mac80211/ieee80211.c
+++ wl2_6_24_rc8_ev/net/mac80211/ieee80211.c
@@ -411,6 +411,329 @@ static int ieee80211_stop(struct net_dev
return 0;
}

+int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid)
+{
+ struct ieee80211_local *local = hw_to_local(hw);
+ struct sta_info *sta;
+ struct ieee80211_sub_if_data *sdata;
+ u16 start_seq_num = 0;
+ u8 *state;
+ int ret;
+ DECLARE_MAC_BUF(mac);
+
+ if (tid >= STA_TID_NUM)
+ return -EINVAL;
+
+#ifdef CONFIG_MAC80211_HT_DEBUG
+ printk(KERN_DEBUG "Open BA session requested for %s tid %u\n",
+ print_mac(mac, ra), tid);
+#endif /* CONFIG_MAC80211_HT_DEBUG */
+
+ sta = sta_info_get(local, ra);
+ if (!sta) {
+ printk(KERN_DEBUG "Could not find the station\n");
+ return -ENOENT;
+ }
+
+ spin_lock_bh(&sta->ampdu_mlme.ampdu_tx);
+
+ /* we have tried too many times, receiver does not want A-MPDU */
+ if (sta->ampdu_mlme.tid_tx[tid].addba_req_num > HT_AGG_MAX_RETRIES) {
+ ret = -EBUSY;
+ goto start_ba_exit;
+ }
+
+ state = &sta->ampdu_mlme.tid_tx[tid].state;
+ /* check if the TID is not in aggregation flow already */
+ if (*state != HT_AGG_STATE_IDLE) {
+#ifdef CONFIG_MAC80211_HT_DEBUG
+ printk(KERN_DEBUG "BA request denied - session is not "
+ "idle on tid %u\n", tid);
+#endif /* CONFIG_MAC80211_HT_DEBUG */
+ ret = -EAGAIN;
+ goto start_ba_exit;
+ }
+
+ /* ensure that TX flow won't interrupt us
+ * until the end of the call to requeue function */
+ spin_lock_bh(&local->mdev->queue_lock);
+
+ /* create a new queue for this aggregation */
+ /* ret = ieee80211_ht_agg_queue_add(local, sta, tid); */
+
+ /* case no queue is available to aggregation
+ * don't switch to aggregation */
+ if (ret) {
+#ifdef CONFIG_MAC80211_HT_DEBUG
+ printk(KERN_DEBUG "BA request denied - no queue available for"
+ " tid %d\n", tid);
+#endif /* CONFIG_MAC80211_HT_DEBUG */
+ spin_unlock_bh(&local->mdev->queue_lock);
+ goto start_ba_exit;
+ }
+ sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev);
+
+ /* Ok, the Addba frame hasn't been sent yet, but if the driver calls the
+ * call back right away, it must see that the flow has begun */
+ *state |= HT_ADDBA_REQUESTED_MSK;
+
+ if (local->ops->ampdu_action)
+ ret = local->ops->ampdu_action(hw, IEEE80211_AMPDU_TX_START,
+ ra, tid, &start_seq_num);
+
+ if (ret) {
+ /* No need to requeue the packets in the agg queue, since we
+ * held the tx lock: no packet could be enqueued to the newly
+ * allocated queue */
+ /* ieee80211_ht_agg_queue_remove(local, sta, tid, 0); */
+#ifdef CONFIG_MAC80211_HT_DEBUG
+ printk(KERN_DEBUG "BA request denied - HW or queue unavailable"
+ " for tid %d\n", tid);
+#endif /* CONFIG_MAC80211_HT_DEBUG */
+ spin_unlock_bh(&local->mdev->queue_lock);
+ *state = HT_AGG_STATE_IDLE;
+ goto start_ba_exit;
+ }
+
+ /* Will put all the packets in the new SW queue */
+ /* ieee80211_requeue(local, ieee802_1d_to_ac[tid]); */
+ spin_unlock_bh(&local->mdev->queue_lock);
+
+ /* We have most probably almost emptied the legacy queue */
+ /* ieee80211_wake_queue(local_to_hw(local), ieee802_1d_to_ac[tid]); */
+
+ /* send an addBA request */
+ sta->ampdu_mlme.dialog_token_allocator++;
+ sta->ampdu_mlme.tid_tx[tid].dialog_token =
+ sta->ampdu_mlme.dialog_token_allocator;
+ sta->ampdu_mlme.tid_tx[tid].ssn = start_seq_num;
+
+ ieee80211_send_addba_request(sta->dev, ra, tid,
+ sta->ampdu_mlme.tid_tx[tid].dialog_token,
+ sta->ampdu_mlme.tid_tx[tid].ssn,
+ 0x40, 5000);
+
+ /* activate the timer for the recipient's addBA response */
+ sta->ampdu_mlme.tid_tx[tid].addba_resp_timer.expires =
+ jiffies + ADDBA_RESP_INTERVAL;
+ add_timer(&sta->ampdu_mlme.tid_tx[tid].addba_resp_timer);
+ printk(KERN_DEBUG "activated addBA response timer on tid %d\n", tid);
+
+start_ba_exit:
+ spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx);
+ sta_info_put(sta);
+ return ret;
+}
+EXPORT_SYMBOL(ieee80211_start_tx_ba_session);
+
+int ieee80211_stop_tx_ba_session(struct ieee80211_hw *hw,
+ u8 *ra, u16 tid,
+ enum ieee80211_back_parties initiator)
+{
+ struct ieee80211_local *local = hw_to_local(hw);
+ struct sta_info *sta;
+ u8 *state;
+ int ret = 0;
+ DECLARE_MAC_BUF(mac);
+
+ if (tid >= STA_TID_NUM)
+ return -EINVAL;
+
+#ifdef CONFIG_MAC80211_HT_DEBUG
+ printk(KERN_DEBUG "Stop a BA session requested for %s tid %u\n",
+ print_mac(mac, ra), tid);
+#endif /* CONFIG_MAC80211_HT_DEBUG */
+
+ sta = sta_info_get(local, ra);
+ if (!sta)
+ return -ENOENT;
+
+ /* check if the TID is in aggregation */
+ state = &sta->ampdu_mlme.tid_tx[tid].state;
+ spin_lock_bh(&sta->ampdu_mlme.ampdu_tx);
+
+ if (*state != HT_AGG_STATE_OPERATIONAL) {
+#ifdef CONFIG_MAC80211_HT_DEBUG
+ printk(KERN_DEBUG "Try to stop Tx aggregation on"
+ " non active TID\n");
+#endif /* CONFIG_MAC80211_HT_DEBUG */
+ ret = -ENOENT;
+ goto stop_BA_exit;
+ }
+
+ ieee80211_stop_queue(hw, sta->tid_to_tx_q[tid]);
+
+ *state = HT_AGG_STATE_REQ_STOP_BA_MSK |
+ (initiator << HT_AGG_STATE_INITIATOR_SHIFT);
+
+ if (local->ops->ampdu_action)
+ ret = local->ops->ampdu_action(hw, IEEE80211_AMPDU_TX_STOP,
+ ra, tid, NULL);
+
+ /* case HW denied going back to legacy */
+ if (ret) {
+ WARN_ON(ret != -EBUSY);
+ *state = HT_AGG_STATE_OPERATIONAL;
+ ieee80211_wake_queue(hw, sta->tid_to_tx_q[tid]);
+ goto stop_BA_exit;
+ }
+
+stop_BA_exit:
+ spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx);
+ sta_info_put(sta);
+ return ret;
+}
+EXPORT_SYMBOL(ieee80211_stop_tx_ba_session);
+
+void ieee80211_start_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u16 tid)
+{
+ struct ieee80211_local *local = hw_to_local(hw);
+ struct sta_info *sta;
+ u8 *state;
+ DECLARE_MAC_BUF(mac);
+
+ if (tid >= STA_TID_NUM) {
+ printk(KERN_DEBUG "Bad TID value: tid = %d (>= %d)\n",
+ tid, STA_TID_NUM);
+ return;
+ }
+
+ sta = sta_info_get(local, ra);
+ if (!sta) {
+ printk(KERN_DEBUG "Could not find station: %s\n",
+ print_mac(mac, ra));
+ return;
+ }
+
+ state = &sta->ampdu_mlme.tid_tx[tid].state;
+ spin_lock_bh(&sta->ampdu_mlme.ampdu_tx);
+
+ if (!(*state & HT_ADDBA_REQUESTED_MSK)) {
+ printk(KERN_DEBUG "addBA was not requested yet, state is %d\n",
+ *state);
+ spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx);
+ sta_info_put(sta);
+ return;
+ }
+
+ WARN_ON_ONCE(*state & HT_ADDBA_DRV_READY_MSK);
+
+ *state |= HT_ADDBA_DRV_READY_MSK;
+
+ if (*state == HT_AGG_STATE_OPERATIONAL) {
+ printk(KERN_DEBUG "Aggregation is on for tid %d \n", tid);
+ ieee80211_wake_queue(hw, sta->tid_to_tx_q[tid]);
+ }
+ spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx);
+ sta_info_put(sta);
+}
+EXPORT_SYMBOL(ieee80211_start_tx_ba_cb);
+
+void ieee80211_stop_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u8 tid)
+{
+ struct ieee80211_local *local = hw_to_local(hw);
+ struct sta_info *sta;
+ u8 *state;
+ int agg_queue;
+ DECLARE_MAC_BUF(mac);
+
+ if (tid >= STA_TID_NUM) {
+ printk(KERN_DEBUG "Bad TID value: tid = %d (>= %d)\n",
+ tid, STA_TID_NUM);
+ return;
+ }
+
+ printk(KERN_DEBUG "Stop a BA session requested on DA %s tid %d\n",
+ print_mac(mac, ra), tid);
+
+ sta = sta_info_get(local, ra);
+ if (!sta) {
+ printk(KERN_DEBUG "Could not find station: %s\n",
+ print_mac(mac, ra));
+ return;
+ }
+ state = &sta->ampdu_mlme.tid_tx[tid].state;
+
+ spin_lock_bh(&sta->ampdu_mlme.ampdu_tx);
+ if ((*state & HT_AGG_STATE_REQ_STOP_BA_MSK) == 0) {
+ printk(KERN_DEBUG "unexpected callback to A-MPDU stop\n");
+ sta_info_put(sta);
+ spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx);
+ return;
+ }
+
+ if (*state & HT_AGG_STATE_INITIATOR_MSK)
+ ieee80211_send_delba(sta->dev, ra, tid,
+ WLAN_BACK_INITIATOR, WLAN_REASON_QSTA_NOT_USE);
+
+ agg_queue = sta->tid_to_tx_q[tid];
+
+ /* avoid ordering issues: we are the only one that can modify
+ * the content of the qdiscs */
+ spin_lock_bh(&local->mdev->queue_lock);
+ /* remove the queue for this aggregation */
+ /* ieee80211_ht_agg_queue_remove(local, sta, tid, 1); */
+ spin_unlock_bh(&local->mdev->queue_lock);
+
+ /* we just requeued the all the frames that were in the removed
+ * queue, and since we might miss a softirq we do netif_schedule.
+ * ieee80211_wake_queue is not used here as this queue is not
+ * necessarily stopped */
+ netif_schedule(local->mdev);
+ *state = HT_AGG_STATE_IDLE;
+ sta->ampdu_mlme.tid_tx[tid].addba_req_num = 0;
+ spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx);
+
+ sta_info_put(sta);
+}
+EXPORT_SYMBOL(ieee80211_stop_tx_ba_cb);
+
+void ieee80211_start_tx_ba_cb_irqsafe(struct ieee80211_hw *hw,
+ const u8 *ra, u16 tid)
+{
+ struct ieee80211_local *local = hw_to_local(hw);
+ struct ieee80211_ra_tid *ra_tid;
+ struct sk_buff *skb = dev_alloc_skb(0);
+
+ if (unlikely(!skb)) {
+ if (net_ratelimit())
+ printk(KERN_WARNING "%s: Not enough memory, "
+ "dropping start BA session", skb->dev->name);
+ return;
+ }
+ ra_tid = (struct ieee80211_ra_tid *) &skb->cb;
+ memcpy(&ra_tid->ra, ra, ETH_ALEN);
+ ra_tid->tid = tid;
+
+ skb->pkt_type = IEEE80211_ADDBA_MSG;
+ skb_queue_tail(&local->skb_queue, skb);
+ tasklet_schedule(&local->tasklet);
+}
+EXPORT_SYMBOL(ieee80211_start_tx_ba_cb_irqsafe);
+
+void ieee80211_stop_tx_ba_cb_irqsafe(struct ieee80211_hw *hw,
+ const u8 *ra, u16 tid)
+{
+ struct ieee80211_local *local = hw_to_local(hw);
+ struct ieee80211_ra_tid *ra_tid;
+ struct sk_buff *skb = dev_alloc_skb(0);
+
+ if (unlikely(!skb)) {
+ if (net_ratelimit())
+ printk(KERN_WARNING "%s: Not enough memory, "
+ "dropping stop BA session", skb->dev->name);
+ return;
+ }
+ ra_tid = (struct ieee80211_ra_tid *) &skb->cb;
+ memcpy(&ra_tid->ra, ra, ETH_ALEN);
+ ra_tid->tid = tid;
+
+ skb->pkt_type = IEEE80211_DELBA_MSG;
+ skb_queue_tail(&local->skb_queue, skb);
+ tasklet_schedule(&local->tasklet);
+}
+EXPORT_SYMBOL(ieee80211_stop_tx_ba_cb_irqsafe);
+
static void ieee80211_set_multicast_list(struct net_device *dev)
{
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
@@ -710,6 +1033,7 @@ static void ieee80211_tasklet_handler(un
struct sk_buff *skb;
struct ieee80211_rx_status rx_status;
struct ieee80211_tx_status *tx_status;
+ struct ieee80211_ra_tid *ra_tid;

while ((skb = skb_dequeue(&local->skb_queue)) ||
(skb = skb_dequeue(&local->skb_queue_unreliable))) {
@@ -730,6 +1054,18 @@ static void ieee80211_tasklet_handler(un
skb, tx_status);
kfree(tx_status);
break;
+ case IEEE80211_DELBA_MSG:
+ ra_tid = (struct ieee80211_ra_tid *) &skb->cb;
+ ieee80211_stop_tx_ba_cb(local_to_hw(local),
+ ra_tid->ra, ra_tid->tid);
+ dev_kfree_skb(skb);
+ break;
+ case IEEE80211_ADDBA_MSG:
+ ra_tid = (struct ieee80211_ra_tid *) &skb->cb;
+ ieee80211_start_tx_ba_cb(local_to_hw(local),
+ ra_tid->ra, ra_tid->tid);
+ dev_kfree_skb(skb);
+ break ;
default: /* should never get here! */
printk(KERN_ERR "%s: Unknown message type (%d)\n",
wiphy_name(local->hw.wiphy), skb->pkt_type);
Index: wl2_6_24_rc8_ev/net/mac80211/ieee80211_i.h
===================================================================
--- wl2_6_24_rc8_ev.orig/net/mac80211/ieee80211_i.h
+++ wl2_6_24_rc8_ev/net/mac80211/ieee80211_i.h
@@ -407,6 +407,8 @@ struct ieee80211_sub_if_data *vif_to_sda
enum {
IEEE80211_RX_MSG = 1,
IEEE80211_TX_STATUS_MSG = 2,
+ IEEE80211_DELBA_MSG = 3,
+ IEEE80211_ADDBA_MSG = 4,
};

struct ieee80211_local {
@@ -627,6 +629,12 @@ struct ieee80211_local {
#endif
};

+/* this struct represents 802.11n's RA/TID combination */
+struct ieee80211_ra_tid {
+ u8 ra[ETH_ALEN];
+ u16 tid;
+};
+
static inline struct ieee80211_local *hw_to_local(
struct ieee80211_hw *hw)
{
@@ -782,9 +790,15 @@ int ieee80211_ht_cap_ie_to_ht_info(struc
int ieee80211_ht_addt_info_ie_to_ht_bss_info(
struct ieee80211_ht_addt_info *ht_add_info_ie,
struct ieee80211_ht_bss_info *bss_info);
+void ieee80211_send_addba_request(struct net_device *dev, const u8 *da,
+ u16 tid, u8 dialog_token, u16 start_seq_num,
+ u16 agg_size, u16 timeout);
+void ieee80211_send_delba(struct net_device *dev, const u8 *da, u16 tid,
+ u16 initiator, u16 reason_code);
void ieee80211_sta_stop_rx_ba_session(struct net_device *dev, u8 *da,
u16 tid, u16 initiator, u16 reason);
void sta_rx_agg_session_timer_expired(unsigned long data);
+void sta_addba_resp_timer_expired(unsigned long data);
/* ieee80211_iface.c */
int ieee80211_if_add(struct net_device *dev, const char *name,
struct net_device **new_dev, int type);
Index: wl2_6_24_rc8_ev/net/mac80211/ieee80211_sta.c
===================================================================
--- wl2_6_24_rc8_ev.orig/net/mac80211/ieee80211_sta.c
+++ wl2_6_24_rc8_ev/net/mac80211/ieee80211_sta.c
@@ -1044,6 +1044,58 @@ static void ieee80211_send_addba_resp(st
return;
}

+void ieee80211_send_addba_request(struct net_device *dev, const u8 *da,
+ u16 tid, u8 dialog_token, u16 start_seq_num,
+ u16 agg_size, u16 timeout)
+{
+ struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct ieee80211_if_sta *ifsta = &sdata->u.sta;
+ struct sk_buff *skb;
+ struct ieee80211_mgmt *mgmt;
+ u16 capab;
+
+ skb = dev_alloc_skb(sizeof(*mgmt) + local->hw.extra_tx_headroom + 1 +
+ sizeof(mgmt->u.action.u.addba_req));
+
+
+ if (!skb) {
+ printk(KERN_ERR "%s: failed to allocate buffer "
+ "for addba request frame\n", dev->name);
+ return;
+ }
+ skb_reserve(skb, local->hw.extra_tx_headroom);
+ mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24);
+ memset(mgmt, 0, 24);
+ memcpy(mgmt->da, da, ETH_ALEN);
+ memcpy(mgmt->sa, dev->dev_addr, ETH_ALEN);
+ if (sdata->vif.type == IEEE80211_IF_TYPE_AP)
+ memcpy(mgmt->bssid, dev->dev_addr, ETH_ALEN);
+ else
+ memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN);
+
+ mgmt->frame_control = IEEE80211_FC(IEEE80211_FTYPE_MGMT,
+ IEEE80211_STYPE_ACTION);
+
+ skb_put(skb, 1 + sizeof(mgmt->u.action.u.addba_req));
+
+ mgmt->u.action.category = WLAN_CATEGORY_BACK;
+ mgmt->u.action.u.addba_req.action_code = WLAN_ACTION_ADDBA_REQ;
+
+ mgmt->u.action.u.addba_req.dialog_token = dialog_token;
+ capab = (u16)(1 << 1); /* bit 1 aggregation policy */
+ capab |= (u16)(tid << 2); /* bit 5:2 TID number */
+ capab |= (u16)(agg_size << 6); /* bit 15:6 max size of aggergation */
+
+ mgmt->u.action.u.addba_req.capab = cpu_to_le16(capab);
+
+ mgmt->u.action.u.addba_req.timeout = cpu_to_le16(timeout);
+ mgmt->u.action.u.addba_req.start_seq_num =
+ cpu_to_le16(start_seq_num << 4);
+
+ ieee80211_sta_tx(dev, skb, 0);
+}
+
static void ieee80211_sta_process_addba_request(struct net_device *dev,
struct ieee80211_mgmt *mgmt,
size_t len)
@@ -1154,8 +1206,80 @@ end_no_lock:
sta_info_put(sta);
}

-static void ieee80211_send_delba(struct net_device *dev, const u8 *da, u16 tid,
- u16 initiator, u16 reason_code)
+static void ieee80211_sta_process_addba_resp(struct net_device *dev,
+ struct ieee80211_mgmt *mgmt,
+ size_t len)
+{
+ struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+ struct ieee80211_hw *hw = &local->hw;
+ struct sta_info *sta;
+ u16 capab;
+ u16 tid;
+ u8 *state;
+
+ sta = sta_info_get(local, mgmt->sa);
+ if (!sta)
+ return;
+
+ capab = le16_to_cpu(mgmt->u.action.u.addba_resp.capab);
+ tid = (capab & IEEE80211_ADDBA_PARAM_TID_MASK) >> 2;
+
+ state = &sta->ampdu_mlme.tid_tx[tid].state;
+
+ spin_lock_bh(&sta->ampdu_mlme.ampdu_tx);
+
+ if (mgmt->u.action.u.addba_resp.dialog_token !=
+ sta->ampdu_mlme.tid_tx[tid].dialog_token) {
+ spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx);
+#ifdef CONFIG_MAC80211_HT_DEBUG
+ printk(KERN_DEBUG "wrong addBA response token, tid %d\n", tid);
+#endif /* CONFIG_MAC80211_HT_DEBUG */
+ sta_info_put(sta);
+ return;
+ }
+
+ del_timer_sync(&sta->ampdu_mlme.tid_tx[tid].addba_resp_timer);
+#ifdef CONFIG_MAC80211_HT_DEBUG
+ printk(KERN_DEBUG "switched off addBA timer for tid %d \n", tid);
+#endif /* CONFIG_MAC80211_HT_DEBUG */
+ if (le16_to_cpu(mgmt->u.action.u.addba_resp.status)
+ == WLAN_STATUS_SUCCESS) {
+ if (!(*state & HT_ADDBA_REQUESTED_MSK)) {
+ spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx);
+ printk(KERN_DEBUG "state not HT_ADDBA_REQUESTED_MSK:"
+ "%d\n", *state);
+ sta_info_put(sta);
+ return;
+ }
+
+ if (*state & HT_ADDBA_RECEIVED_MSK)
+ printk(KERN_DEBUG "double addBA response\n");
+
+ *state |= HT_ADDBA_RECEIVED_MSK;
+ sta->ampdu_mlme.tid_tx[tid].addba_req_num = 0;
+
+ if (*state == HT_AGG_STATE_OPERATIONAL) {
+ printk(KERN_DEBUG "Aggregation on for tid %d \n", tid);
+ ieee80211_wake_queue(hw, sta->tid_to_tx_q[tid]);
+ }
+
+ spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx);
+ printk(KERN_DEBUG "recipient accepted agg: tid %d \n", tid);
+ } else {
+ printk(KERN_DEBUG "recipient rejected agg: tid %d \n", tid);
+
+ sta->ampdu_mlme.tid_tx[tid].addba_req_num++;
+ /* this will allow the state check in stop_BA_session */
+ *state = HT_AGG_STATE_OPERATIONAL;
+ spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx);
+ ieee80211_stop_tx_ba_session(hw, sta->addr, tid,
+ WLAN_BACK_INITIATOR);
+ }
+ sta_info_put(sta);
+}
+
+void ieee80211_send_delba(struct net_device *dev, const u8 *da, u16 tid,
+ u16 initiator, u16 reason_code)
{
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
@@ -1257,6 +1381,7 @@ void ieee80211_sta_stop_rx_ba_session(st
sta_info_put(sta);
}

+
static void ieee80211_sta_process_delba(struct net_device *dev,
struct ieee80211_mgmt *mgmt, size_t len)
{
@@ -1288,6 +1413,53 @@ static void ieee80211_sta_process_delba(
}

/*
+ * After sending add Block Ack request we activated a timer until
+ * add Block Ack response will arrive from the recipient.
+ * If this timer expires sta_addba_resp_timer_expired will be executed.
+ */
+void sta_addba_resp_timer_expired(unsigned long data)
+{
+ /* not an elegant detour, but there is no choice as the timer passes
+ * only one argument, and both sta_info and TID are needed, so init
+ * flow in sta_info_add gives the TID as data, while the timer_to_id
+ * array gives the sta through container_of */
+ u16 tid = *(int *)data;
+ struct sta_info *temp_sta = container_of((void *)data,
+ struct sta_info, timer_to_tid[tid]);
+
+ struct ieee80211_local *local = temp_sta->local;
+ struct ieee80211_hw *hw = &local->hw;
+ struct sta_info *sta;
+ u8 *state;
+
+ sta = sta_info_get(local, temp_sta->addr);
+ if (!sta)
+ return;
+
+ state = &sta->ampdu_mlme.tid_tx[tid].state;
+ /* check if the TID waits for addBA response */
+ spin_lock_bh(&sta->ampdu_mlme.ampdu_tx);
+ if (!(*state & HT_ADDBA_REQUESTED_MSK)) {
+ spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx);
+ *state = HT_AGG_STATE_IDLE;
+ printk(KERN_DEBUG "timer expired on tid %d but we are not "
+ "expecting addBA response there", tid);
+ goto timer_expired_exit;
+ }
+
+ printk(KERN_DEBUG "addBA response timer expired on tid %d\n", tid);
+
+ /* go through the state check in stop_BA_session */
+ *state = HT_AGG_STATE_OPERATIONAL;
+ spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx);
+ ieee80211_stop_tx_ba_session(hw, temp_sta->addr, tid,
+ WLAN_BACK_INITIATOR);
+
+timer_expired_exit:
+ sta_info_put(sta);
+}
+
+/*
* After receiving Block Ack Request (BAR) we activated a
* timer after each frame arrives from the originator.
* if this timer expires ieee80211_sta_stop_rx_ba_session will be executed.
@@ -2234,6 +2406,12 @@ static void ieee80211_rx_mgmt_action(str
break;
ieee80211_sta_process_addba_request(dev, mgmt, len);
break;
+ case WLAN_ACTION_ADDBA_RESP:
+ if (len < (IEEE80211_MIN_ACTION_SIZE +
+ sizeof(mgmt->u.action.u.addba_resp)))
+ break;
+ ieee80211_sta_process_addba_resp(dev, mgmt, len);
+ break;
case WLAN_ACTION_DELBA:
if (len < (IEEE80211_MIN_ACTION_SIZE +
sizeof(mgmt->u.action.u.delba)))

2008-01-28 12:07:44

by Ron Rindjunsky

[permalink] [raw]
Subject: [PATCH 12/12 v2] iwlwifi: A-MPDU Tx activation by load measures

This patch gives a heuristic for activation of the A-MPDU Tx.
As the rate scaling is rate aware, it now also measures estimated load, and
sends A-MPDU activation after a threshold has been met.

Signed-off-by: Ron Rindjunsky <[email protected]>
---
drivers/net/wireless/iwlwifi/iwl-4965-rs.c | 204 +++++++++++++--
drivers/net/wireless/iwlwifi/iwl-4965-rs.h | 12 +
drivers/net/wireless/iwlwifi/iwl-4965.c | 396 ---------------------------
drivers/net/wireless/iwlwifi/iwl-4965.h | 54 +----
drivers/net/wireless/iwlwifi/iwl4965-base.c | 20 --
5 files changed, 199 insertions(+), 487 deletions(-)

Index: wl2_6_24_rc8_ev/drivers/net/wireless/iwlwifi/iwl-4965-rs.c
===================================================================
--- wl2_6_24_rc8_ev.orig/drivers/net/wireless/iwlwifi/iwl-4965-rs.c
+++ wl2_6_24_rc8_ev/drivers/net/wireless/iwlwifi/iwl-4965-rs.c
@@ -83,7 +83,7 @@ struct iwl4965_rate_scale_data {
/**
* struct iwl4965_scale_tbl_info -- tx params and success history for all rates
*
- * There are two of these in struct iwl_rate_scale_priv,
+ * There are two of these in struct iwl4965_lq_sta,
* one for "active", and one for "search".
*/
struct iwl4965_scale_tbl_info {
@@ -98,8 +98,23 @@ struct iwl4965_scale_tbl_info {
struct iwl4965_rate_scale_data win[IWL_RATE_COUNT]; /* rate histories */
};

+#ifdef CONFIG_IWL4965_HT
+
+struct iwl4965_traffic_load {
+ unsigned long time_stamp; /* age of the oldest statistics */
+ u32 packet_count[TID_QUEUE_MAX_SIZE]; /* packet count in this time
+ * slice */
+ u32 total; /* total num of packets during the
+ * last TID_MAX_TIME_DIFF */
+ u8 queue_count; /* number of queues that has
+ * been used since the last cleanup */
+ u8 head; /* start of the circular buffer */
+};
+
+#endif /* CONFIG_IWL4965_HT */
+
/**
- * struct iwl_rate_scale_priv -- driver's rate scaling private structure
+ * struct iwl4965_lq_sta -- driver's rate scaling private structure
*
* Pointer to this gets passed back and forth between driver and mac80211.
*/
@@ -136,9 +151,16 @@ struct iwl4965_lq_sta {

struct iwl4965_link_quality_cmd lq;
struct iwl4965_scale_tbl_info lq_info[LQ_SIZE]; /* "active", "search" */
+#ifdef CONFIG_IWL4965_HT
+ struct iwl4965_traffic_load load[TID_MAX_LOAD_COUNT];
+ u8 tx_agg_tid_en;
+#endif
#ifdef CONFIG_MAC80211_DEBUGFS
struct dentry *rs_sta_dbgfs_scale_table_file;
struct dentry *rs_sta_dbgfs_stats_table_file;
+#ifdef CONFIG_IWL4965_HT
+ struct dentry *rs_sta_dbgfs_tx_agg_tid_en_file;
+#endif
struct iwl4965_rate dbg_fixed;
struct iwl4965_priv *drv;
#endif
@@ -269,6 +291,135 @@ static void rs_rate_scale_clear_window(s
window->stamp = 0;
}

+#ifdef CONFIG_IWL4965_HT
+/*
+ * removes the old data from the statistics. All data that is older than
+ * TID_MAX_TIME_DIFF, will be deleted.
+ */
+static void rs_tl_rm_old_stats(struct iwl4965_traffic_load *tl, u32 curr_time)
+{
+ /* The oldest age we want to keep */
+ u32 oldest_time = curr_time - TID_MAX_TIME_DIFF;
+
+ while (tl->queue_count &&
+ (tl->time_stamp < oldest_time)) {
+ tl->total -= tl->packet_count[tl->head];
+ tl->packet_count[tl->head] = 0;
+ tl->time_stamp += TID_QUEUE_CELL_SPACING;
+ tl->queue_count--;
+ tl->head++;
+ if (tl->head >= TID_QUEUE_MAX_SIZE)
+ tl->head = 0;
+ }
+}
+
+/*
+ * increment traffic load value for tid and also remove
+ * any old values if passed the certain time period
+ */
+static void rs_tl_add_packet(struct iwl4965_lq_sta *lq_data, u8 tid)
+{
+ u32 curr_time = jiffies_to_msecs(jiffies);
+ u32 time_diff;
+ s32 index;
+ struct iwl4965_traffic_load *tl = NULL;
+
+ if (tid >= TID_MAX_LOAD_COUNT)
+ return;
+
+ tl = &lq_data->load[tid];
+
+ curr_time -= curr_time % TID_ROUND_VALUE;
+
+ /* Happens only for the first packet. Initialize the data */
+ if (!(tl->queue_count)) {
+ tl->total = 1;
+ tl->time_stamp = curr_time;
+ tl->queue_count = 1;
+ tl->head = 0;
+ tl->packet_count[0] = 1;
+ return;
+ }
+
+ time_diff = TIME_WRAP_AROUND(tl->time_stamp, curr_time);
+ index = time_diff / TID_QUEUE_CELL_SPACING;
+
+ /* The history is too long: remove data that is older than */
+ /* TID_MAX_TIME_DIFF */
+ if (index >= TID_QUEUE_MAX_SIZE)
+ rs_tl_rm_old_stats(tl, curr_time);
+
+ index = (tl->head + index) % TID_QUEUE_MAX_SIZE;
+ tl->packet_count[index] = tl->packet_count[index] + 1;
+ tl->total = tl->total + 1;
+
+ if ((index + 1) > tl->queue_count)
+ tl->queue_count = index + 1;
+}
+
+/*
+ get the traffic load value for tid
+*/
+static u32 rs_tl_get_load(struct iwl4965_lq_sta *lq_data, u8 tid)
+{
+ u32 curr_time = jiffies_to_msecs(jiffies);
+ u32 time_diff;
+ s32 index;
+ struct iwl4965_traffic_load *tl = NULL;
+
+ if (tid >= TID_MAX_LOAD_COUNT)
+ return 0;
+
+ tl = &(lq_data->load[tid]);
+
+ curr_time -= curr_time % TID_ROUND_VALUE;
+
+ if (!(tl->queue_count))
+ return 0;
+
+ time_diff = TIME_WRAP_AROUND(tl->time_stamp, curr_time);
+ index = time_diff / TID_QUEUE_CELL_SPACING;
+
+ /* The history is too long: remove data that is older than */
+ /* TID_MAX_TIME_DIFF */
+ if (index >= TID_QUEUE_MAX_SIZE)
+ rs_tl_rm_old_stats(tl, curr_time);
+
+ return tl->total;
+}
+
+static void rs_tl_turn_on_agg_for_tid(struct iwl4965_priv *priv,
+ struct iwl4965_lq_sta *lq_data, u8 tid,
+ struct sta_info *sta)
+{
+ unsigned long state;
+ DECLARE_MAC_BUF(mac);
+
+ spin_lock_bh(&sta->ampdu_mlme.ampdu_tx);
+ state = sta->ampdu_mlme.tid_tx[tid].state;
+ spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx);
+
+ if (state == HT_AGG_STATE_IDLE &&
+ rs_tl_get_load(lq_data, tid) > IWL_AGG_LOAD_THRESHOLD) {
+ IWL_DEBUG_HT("Starting Tx agg: STA: %s tid: %d\n",
+ print_mac(mac, sta->addr), tid);
+ ieee80211_start_tx_ba_session(priv->hw, sta->addr, tid);
+ }
+}
+
+static void rs_tl_turn_on_agg(struct iwl4965_priv *priv, u8 tid,
+ struct iwl4965_lq_sta *lq_data,
+ struct sta_info *sta)
+{
+ if ((tid < TID_MAX_LOAD_COUNT))
+ rs_tl_turn_on_agg_for_tid(priv, lq_data, tid, sta);
+ else if (tid == IWL_AGG_ALL_TID)
+ for (tid = 0; tid < TID_MAX_LOAD_COUNT; tid++)
+ rs_tl_turn_on_agg_for_tid(priv, lq_data, tid, sta);
+}
+
+#endif /* CONFIG_IWLWIFI_HT */
+
/**
* rs_collect_tx_data - Update the success/failure sliding window
*
@@ -1134,7 +1285,7 @@ static int rs_switch_to_mimo(struct iwl4
return 0;
#else
return -1;
-#endif /*CONFIG_IWL4965_HT */
+#endif /*CONFIG_IWL4965_HT */
}

/*
@@ -1197,7 +1348,7 @@ static int rs_switch_to_siso(struct iwl4
#else
return -1;

-#endif /*CONFIG_IWL4965_HT */
+#endif /*CONFIG_IWL4965_HT */
}

/*
@@ -1354,6 +1505,7 @@ static int rs_move_siso_to_other(struct
break;
case IWL_SISO_SWITCH_GI:
IWL_DEBUG_HT("LQ: SISO SWITCH TO GI\n");
+
memcpy(search_tbl, tbl, sz);
search_tbl->action = 0;
if (search_tbl->is_SGI)
@@ -1419,6 +1571,7 @@ static int rs_move_mimo_to_other(struct
case IWL_MIMO_SWITCH_ANTENNA_B:
IWL_DEBUG_HT("LQ: MIMO SWITCH TO SISO\n");

+
/* Set up new search table for SISO */
memcpy(search_tbl, tbl, sz);
search_tbl->lq_type = LQ_SISO;
@@ -1603,6 +1756,10 @@ static void rs_rate_scale_perform(struct
u8 active_tbl = 0;
u8 done_search = 0;
u16 high_low;
+#ifdef CONFIG_IWL4965_HT
+ u8 tid = MAX_TID_COUNT;
+ __le16 *qc;
+#endif

IWL_DEBUG_RATE("rate scale calculate new rate for skb\n");

@@ -1623,6 +1780,13 @@ static void rs_rate_scale_perform(struct
}
lq_sta = (struct iwl4965_lq_sta *)sta->rate_ctrl_priv;

+#ifdef CONFIG_IWL4965_HT
+ qc = ieee80211_get_qos_ctrl(hdr);
+ if (qc) {
+ tid = (u8)(le16_to_cpu(*qc) & 0xf);
+ rs_tl_add_packet(lq_sta, tid);
+ }
+#endif
/*
* Select rate-scale / modulation-mode table to work with in
* the rest of this function: "search" if searching for better
@@ -1943,15 +2107,14 @@ static void rs_rate_scale_perform(struct
* mode for a while before next round of mode comparisons. */
if (lq_sta->enable_counter &&
(lq_sta->action_counter >= IWL_ACTION_LIMIT)) {
-#ifdef CONFIG_IWL4965_HT_AGG
- /* If appropriate, set up aggregation! */
- if ((lq_sta->last_tpt > TID_AGG_TPT_THREHOLD) &&
- (priv->lq_mngr.agg_ctrl.auto_agg)) {
- priv->lq_mngr.agg_ctrl.tid_retry =
- TID_ALL_SPECIFIED;
- schedule_work(&priv->agg_work);
+#ifdef CONFIG_IWL4965_HT
+ if ((lq_sta->last_tpt > IWL_AGG_TPT_THREHOLD) &&
+ (lq_sta->tx_agg_tid_en & (1 << tid)) &&
+ (tid != MAX_TID_COUNT)) {
+ IWL_DEBUG_HT("try to aggregate tid %d\n", tid);
+ rs_tl_turn_on_agg(priv, tid, lq_sta, sta);
}
-#endif /*CONFIG_IWL4965_HT_AGG */
+#endif /*CONFIG_IWL4965_HT */
lq_sta->action_counter = 0;
rs_set_stay_in_table(0, lq_sta);
}
@@ -2209,6 +2372,8 @@ static void rs_rate_init(void *priv_rate
IWL_DEBUG_HT("SISO RATE 0x%X MIMO RATE 0x%X\n",
lq_sta->active_siso_rate,
lq_sta->active_mimo_rate);
+ /* as default allow aggregation for all tids */
+ lq_sta->tx_agg_tid_en = IWL_AGG_ALL_TID;
#endif /*CONFIG_IWL4965_HT*/
#ifdef CONFIG_MAC80211_DEBUGFS
lq_sta->drv = priv;
@@ -2352,12 +2517,6 @@ static void rs_clear(void *priv_rate)
IWL_DEBUG_RATE("enter\n");

priv->lq_mngr.lq_ready = 0;
-#ifdef CONFIG_IWL4965_HT
-#ifdef CONFIG_IWL4965_HT_AGG
- if (priv->lq_mngr.agg_ctrl.granted_ba)
- iwl4965_turn_off_agg(priv, TID_ALL_SPECIFIED);
-#endif /*CONFIG_IWL4965_HT_AGG */
-#endif /* CONFIG_IWL4965_HT */

IWL_DEBUG_RATE("leave\n");
}
@@ -2524,6 +2683,12 @@ static void rs_add_debugfs(void *priv, v
lq_sta->rs_sta_dbgfs_stats_table_file =
debugfs_create_file("rate_stats_table", 0600, dir,
lq_sta, &rs_sta_dbgfs_stats_table_ops);
+#ifdef CONFIG_IWL4965_HT
+ lq_sta->rs_sta_dbgfs_tx_agg_tid_en_file =
+ debugfs_create_u8("tx_agg_tid_enable", 0600, dir,
+ &lq_sta->tx_agg_tid_en);
+#endif
+
}

static void rs_remove_debugfs(void *priv, void *priv_sta)
@@ -2531,6 +2696,9 @@ static void rs_remove_debugfs(void *priv
struct iwl4965_lq_sta *lq_sta = priv_sta;
debugfs_remove(lq_sta->rs_sta_dbgfs_scale_table_file);
debugfs_remove(lq_sta->rs_sta_dbgfs_stats_table_file);
+#ifdef CONFIG_IWL4965_HT
+ debugfs_remove(lq_sta->rs_sta_dbgfs_tx_agg_tid_en_file);
+#endif
}
#endif

Index: wl2_6_24_rc8_ev/drivers/net/wireless/iwlwifi/iwl-4965-rs.h
===================================================================
--- wl2_6_24_rc8_ev.orig/drivers/net/wireless/iwlwifi/iwl-4965-rs.h
+++ wl2_6_24_rc8_ev/drivers/net/wireless/iwlwifi/iwl-4965-rs.h
@@ -212,6 +212,18 @@ enum {

#define LQ_SIZE 2 /* 2 mode tables: "Active" and "Search" */

+/* load per tid defines for A-MPDU activation */
+#define IWL_AGG_TPT_THREHOLD 0
+#define IWL_AGG_LOAD_THRESHOLD 10
+#define IWL_AGG_ALL_TID 0xff
+#define TID_QUEUE_CELL_SPACING 50 /*mS */
+#define TID_QUEUE_MAX_SIZE 20
+#define TID_ROUND_VALUE 5 /* mS */
+#define TID_MAX_LOAD_COUNT 8
+
+#define TID_MAX_TIME_DIFF ((TID_QUEUE_MAX_SIZE - 1) * TID_QUEUE_CELL_SPACING)
+#define TIME_WRAP_AROUND(x, y) (((y) > (x)) ? (y) - (x) : (0-(x)) + (y))
+
extern const struct iwl4965_rate_info iwl4965_rates[IWL_RATE_COUNT];

enum iwl4965_table_type {
Index: wl2_6_24_rc8_ev/drivers/net/wireless/iwlwifi/iwl-4965.c
===================================================================
--- wl2_6_24_rc8_ev.orig/drivers/net/wireless/iwlwifi/iwl-4965.c
+++ wl2_6_24_rc8_ev/drivers/net/wireless/iwlwifi/iwl-4965.c
@@ -2946,378 +2946,6 @@ void iwl4965_set_rxon_chain(struct iwl49
IWL_DEBUG_ASSOC("rx chain %X\n", priv->staging_rxon.rx_chain);
}

-#ifdef CONFIG_IWL4965_HT
-#ifdef CONFIG_IWL4965_HT_AGG
-/*
- get the traffic load value for tid
-*/
-static u32 iwl4965_tl_get_load(struct iwl4965_priv *priv, u8 tid)
-{
- u32 load = 0;
- u32 current_time = jiffies_to_msecs(jiffies);
- u32 time_diff;
- s32 index;
- unsigned long flags;
- struct iwl4965_traffic_load *tid_ptr = NULL;
-
- if (tid >= TID_MAX_LOAD_COUNT)
- return 0;
-
- tid_ptr = &(priv->lq_mngr.agg_ctrl.traffic_load[tid]);
-
- current_time -= current_time % TID_ROUND_VALUE;
-
- spin_lock_irqsave(&priv->lq_mngr.lock, flags);
- if (!(tid_ptr->queue_count))
- goto out;
-
- time_diff = TIME_WRAP_AROUND(tid_ptr->time_stamp, current_time);
- index = time_diff / TID_QUEUE_CELL_SPACING;
-
- if (index >= TID_QUEUE_MAX_SIZE) {
- u32 oldest_time = current_time - TID_MAX_TIME_DIFF;
-
- while (tid_ptr->queue_count &&
- (tid_ptr->time_stamp < oldest_time)) {
- tid_ptr->total -= tid_ptr->packet_count[tid_ptr->head];
- tid_ptr->packet_count[tid_ptr->head] = 0;
- tid_ptr->time_stamp += TID_QUEUE_CELL_SPACING;
- tid_ptr->queue_count--;
- tid_ptr->head++;
- if (tid_ptr->head >= TID_QUEUE_MAX_SIZE)
- tid_ptr->head = 0;
- }
- }
- load = tid_ptr->total;
-
- out:
- spin_unlock_irqrestore(&priv->lq_mngr.lock, flags);
- return load;
-}
-
-/*
- increment traffic load value for tid and also remove
- any old values if passed the certian time period
-*/
-static void iwl4965_tl_add_packet(struct iwl4965_priv *priv, u8 tid)
-{
- u32 current_time = jiffies_to_msecs(jiffies);
- u32 time_diff;
- s32 index;
- unsigned long flags;
- struct iwl4965_traffic_load *tid_ptr = NULL;
-
- if (tid >= TID_MAX_LOAD_COUNT)
- return;
-
- tid_ptr = &(priv->lq_mngr.agg_ctrl.traffic_load[tid]);
-
- current_time -= current_time % TID_ROUND_VALUE;
-
- spin_lock_irqsave(&priv->lq_mngr.lock, flags);
- if (!(tid_ptr->queue_count)) {
- tid_ptr->total = 1;
- tid_ptr->time_stamp = current_time;
- tid_ptr->queue_count = 1;
- tid_ptr->head = 0;
- tid_ptr->packet_count[0] = 1;
- goto out;
- }
-
- time_diff = TIME_WRAP_AROUND(tid_ptr->time_stamp, current_time);
- index = time_diff / TID_QUEUE_CELL_SPACING;
-
- if (index >= TID_QUEUE_MAX_SIZE) {
- u32 oldest_time = current_time - TID_MAX_TIME_DIFF;
-
- while (tid_ptr->queue_count &&
- (tid_ptr->time_stamp < oldest_time)) {
- tid_ptr->total -= tid_ptr->packet_count[tid_ptr->head];
- tid_ptr->packet_count[tid_ptr->head] = 0;
- tid_ptr->time_stamp += TID_QUEUE_CELL_SPACING;
- tid_ptr->queue_count--;
- tid_ptr->head++;
- if (tid_ptr->head >= TID_QUEUE_MAX_SIZE)
- tid_ptr->head = 0;
- }
- }
-
- index = (tid_ptr->head + index) % TID_QUEUE_MAX_SIZE;
- tid_ptr->packet_count[index] = tid_ptr->packet_count[index] + 1;
- tid_ptr->total = tid_ptr->total + 1;
-
- if ((index + 1) > tid_ptr->queue_count)
- tid_ptr->queue_count = index + 1;
- out:
- spin_unlock_irqrestore(&priv->lq_mngr.lock, flags);
-
-}
-
-#define MMAC_SCHED_MAX_NUMBER_OF_HT_BACK_FLOWS 7
-enum HT_STATUS {
- BA_STATUS_FAILURE = 0,
- BA_STATUS_INITIATOR_DELBA,
- BA_STATUS_RECIPIENT_DELBA,
- BA_STATUS_RENEW_ADDBA_REQUEST,
- BA_STATUS_ACTIVE,
-};
-
-/**
- * iwl4964_tl_ba_avail - Find out if an unused aggregation queue is available
- */
-static u8 iwl4964_tl_ba_avail(struct iwl4965_priv *priv)
-{
- int i;
- struct iwl4965_lq_mngr *lq;
- u8 count = 0;
- u16 msk;
-
- lq = (struct iwl4965_lq_mngr *)&(priv->lq_mngr);
-
- /* Find out how many agg queues are in use */
- for (i = 0; i < TID_MAX_LOAD_COUNT ; i++) {
- msk = 1 << i;
- if ((lq->agg_ctrl.granted_ba & msk) ||
- (lq->agg_ctrl.wait_for_agg_status & msk))
- count++;
- }
-
- if (count < MMAC_SCHED_MAX_NUMBER_OF_HT_BACK_FLOWS)
- return 1;
-
- return 0;
-}
-
-static void iwl4965_ba_status(struct iwl4965_priv *priv,
- u8 tid, enum HT_STATUS status);
-
-static int iwl4965_perform_addba(struct iwl4965_priv *priv, u8 tid, u32 length,
- u32 ba_timeout)
-{
- int rc;
-
- rc = ieee80211_start_BA_session(priv->hw, priv->bssid, tid);
- if (rc)
- iwl4965_ba_status(priv, tid, BA_STATUS_FAILURE);
-
- return rc;
-}
-
-static int iwl4965_perform_delba(struct iwl4965_priv *priv, u8 tid)
-{
- int rc;
-
- rc = ieee80211_stop_BA_session(priv->hw, priv->bssid, tid);
- if (rc)
- iwl4965_ba_status(priv, tid, BA_STATUS_FAILURE);
-
- return rc;
-}
-
-static void iwl4965_turn_on_agg_for_tid(struct iwl4965_priv *priv,
- struct iwl4965_lq_mngr *lq,
- u8 auto_agg, u8 tid)
-{
- u32 tid_msk = (1 << tid);
- unsigned long flags;
-
- spin_lock_irqsave(&priv->lq_mngr.lock, flags);
-/*
- if ((auto_agg) && (!lq->enable_counter)){
- lq->agg_ctrl.next_retry = 0;
- lq->agg_ctrl.tid_retry = 0;
- spin_unlock_irqrestore(&priv->lq_mngr.lock, flags);
- return;
- }
-*/
- if (!(lq->agg_ctrl.granted_ba & tid_msk) &&
- (lq->agg_ctrl.requested_ba & tid_msk)) {
- u8 available_queues;
- u32 load;
-
- spin_unlock_irqrestore(&priv->lq_mngr.lock, flags);
- available_queues = iwl4964_tl_ba_avail(priv);
- load = iwl4965_tl_get_load(priv, tid);
-
- spin_lock_irqsave(&priv->lq_mngr.lock, flags);
- if (!available_queues) {
- if (auto_agg)
- lq->agg_ctrl.tid_retry |= tid_msk;
- else {
- lq->agg_ctrl.requested_ba &= ~tid_msk;
- lq->agg_ctrl.wait_for_agg_status &= ~tid_msk;
- }
- } else if ((auto_agg) &&
- ((load <= lq->agg_ctrl.tid_traffic_load_threshold) ||
- ((lq->agg_ctrl.wait_for_agg_status & tid_msk))))
- lq->agg_ctrl.tid_retry |= tid_msk;
- else {
- lq->agg_ctrl.wait_for_agg_status |= tid_msk;
- spin_unlock_irqrestore(&priv->lq_mngr.lock, flags);
- iwl4965_perform_addba(priv, tid, 0x40,
- lq->agg_ctrl.ba_timeout);
- spin_lock_irqsave(&priv->lq_mngr.lock, flags);
- }
- }
- spin_unlock_irqrestore(&priv->lq_mngr.lock, flags);
-}
-
-static void iwl4965_turn_on_agg(struct iwl4965_priv *priv, u8 tid)
-{
- struct iwl4965_lq_mngr *lq;
- unsigned long flags;
-
- lq = (struct iwl4965_lq_mngr *)&(priv->lq_mngr);
-
- if ((tid < TID_MAX_LOAD_COUNT))
- iwl4965_turn_on_agg_for_tid(priv, lq, lq->agg_ctrl.auto_agg,
- tid);
- else if (tid == TID_ALL_SPECIFIED) {
- if (lq->agg_ctrl.requested_ba) {
- for (tid = 0; tid < TID_MAX_LOAD_COUNT; tid++)
- iwl4965_turn_on_agg_for_tid(priv, lq,
- lq->agg_ctrl.auto_agg, tid);
- } else {
- spin_lock_irqsave(&priv->lq_mngr.lock, flags);
- lq->agg_ctrl.tid_retry = 0;
- lq->agg_ctrl.next_retry = 0;
- spin_unlock_irqrestore(&priv->lq_mngr.lock, flags);
- }
- }
-
-}
-
-void iwl4965_turn_off_agg(struct iwl4965_priv *priv, u8 tid)
-{
- u32 tid_msk;
- struct iwl4965_lq_mngr *lq;
- unsigned long flags;
-
- lq = (struct iwl4965_lq_mngr *)&(priv->lq_mngr);
-
- if ((tid < TID_MAX_LOAD_COUNT)) {
- tid_msk = 1 << tid;
- spin_lock_irqsave(&priv->lq_mngr.lock, flags);
- lq->agg_ctrl.wait_for_agg_status |= tid_msk;
- lq->agg_ctrl.requested_ba &= ~tid_msk;
- spin_unlock_irqrestore(&priv->lq_mngr.lock, flags);
- iwl4965_perform_delba(priv, tid);
- } else if (tid == TID_ALL_SPECIFIED) {
- spin_lock_irqsave(&priv->lq_mngr.lock, flags);
- for (tid = 0; tid < TID_MAX_LOAD_COUNT; tid++) {
- tid_msk = 1 << tid;
- lq->agg_ctrl.wait_for_agg_status |= tid_msk;
- spin_unlock_irqrestore(&priv->lq_mngr.lock, flags);
- iwl4965_perform_delba(priv, tid);
- spin_lock_irqsave(&priv->lq_mngr.lock, flags);
- }
- lq->agg_ctrl.requested_ba = 0;
- spin_unlock_irqrestore(&priv->lq_mngr.lock, flags);
- }
-}
-
-/**
- * iwl4965_ba_status - Update driver's link quality mgr with tid's HT status
- */
-static void iwl4965_ba_status(struct iwl4965_priv *priv,
- u8 tid, enum HT_STATUS status)
-{
- struct iwl4965_lq_mngr *lq;
- u32 tid_msk = (1 << tid);
- unsigned long flags;
-
- lq = (struct iwl4965_lq_mngr *)&(priv->lq_mngr);
-
- if ((tid >= TID_MAX_LOAD_COUNT))
- goto out;
-
- spin_lock_irqsave(&priv->lq_mngr.lock, flags);
- switch (status) {
- case BA_STATUS_ACTIVE:
- if (!(lq->agg_ctrl.granted_ba & tid_msk))
- lq->agg_ctrl.granted_ba |= tid_msk;
- break;
- default:
- if ((lq->agg_ctrl.granted_ba & tid_msk))
- lq->agg_ctrl.granted_ba &= ~tid_msk;
- break;
- }
-
- lq->agg_ctrl.wait_for_agg_status &= ~tid_msk;
- if (status != BA_STATUS_ACTIVE) {
- if (lq->agg_ctrl.auto_agg) {
- lq->agg_ctrl.tid_retry |= tid_msk;
- lq->agg_ctrl.next_retry =
- jiffies + msecs_to_jiffies(500);
- } else
- lq->agg_ctrl.requested_ba &= ~tid_msk;
- }
- spin_unlock_irqrestore(&priv->lq_mngr.lock, flags);
- out:
- return;
-}
-
-static void iwl4965_bg_agg_work(struct work_struct *work)
-{
- struct iwl4965_priv *priv = container_of(work, struct iwl4965_priv,
- agg_work);
-
- u32 tid;
- u32 retry_tid;
- u32 tid_msk;
- unsigned long flags;
- struct iwl4965_lq_mngr *lq = (struct iwl4965_lq_mngr *)&(priv->lq_mngr);
-
- spin_lock_irqsave(&priv->lq_mngr.lock, flags);
- retry_tid = lq->agg_ctrl.tid_retry;
- lq->agg_ctrl.tid_retry = 0;
- spin_unlock_irqrestore(&priv->lq_mngr.lock, flags);
-
- if (retry_tid == TID_ALL_SPECIFIED)
- iwl4965_turn_on_agg(priv, TID_ALL_SPECIFIED);
- else {
- for (tid = 0; tid < TID_MAX_LOAD_COUNT; tid++) {
- tid_msk = (1 << tid);
- if (retry_tid & tid_msk)
- iwl4965_turn_on_agg(priv, tid);
- }
- }
-
- spin_lock_irqsave(&priv->lq_mngr.lock, flags);
- if (lq->agg_ctrl.tid_retry)
- lq->agg_ctrl.next_retry = jiffies + msecs_to_jiffies(500);
- spin_unlock_irqrestore(&priv->lq_mngr.lock, flags);
- return;
-}
-
-/* TODO: move this functionality to rate scaling */
-void iwl4965_tl_get_stats(struct iwl4965_priv *priv,
- struct ieee80211_hdr *hdr)
-{
- __le16 *qc = ieee80211_get_qos_ctrl(hdr);
-
- if (qc &&
- (priv->iw_mode != IEEE80211_IF_TYPE_IBSS)) {
- u8 tid = 0;
- tid = (u8) (le16_to_cpu(*qc) & 0xF);
- if (tid < TID_MAX_LOAD_COUNT)
- iwl4965_tl_add_packet(priv, tid);
- }
-
- if (priv->lq_mngr.agg_ctrl.next_retry &&
- (time_after(priv->lq_mngr.agg_ctrl.next_retry, jiffies))) {
- unsigned long flags;
-
- spin_lock_irqsave(&priv->lq_mngr.lock, flags);
- priv->lq_mngr.agg_ctrl.next_retry = 0;
- spin_unlock_irqrestore(&priv->lq_mngr.lock, flags);
- schedule_work(&priv->agg_work);
- }
-}
-
-#endif /*CONFIG_IWL4965_HT_AGG */
-#endif /* CONFIG_IWL4965_HT */
-
/**
* sign_extend - Sign extend a value using specified bit as sign-bit
*
@@ -4191,25 +3819,6 @@ static void iwl4965_rx_missed_beacon_not
}

#ifdef CONFIG_IWL4965_HT
-#ifdef CONFIG_IWL4965_HT_AGG
-
-/**
- * iwl4965_set_tx_status - Update driver's record of one Tx frame's status
- *
- * This will get sent to mac80211.
- */
-static void iwl4965_set_tx_status(struct iwl4965_priv *priv, int txq_id, int idx,
- u32 status, u32 retry_count, u32 rate)
-{
- struct ieee80211_tx_status *tx_status =
- &(priv->txq[txq_id].txb[idx].status);
-
- tx_status->flags = status ? IEEE80211_TX_STATUS_ACK : 0;
- tx_status->retry_count += retry_count;
- tx_status->control.tx_rate = rate;
-}
-
-#endif/* CONFIG_IWL4965_HT_AGG */

/**
* iwl4965_sta_modify_enable_tid_tx - Enable Tx for this TID in station table
@@ -4971,11 +4580,6 @@ void iwl4965_hw_setup_deferred_work(stru
#ifdef CONFIG_IWL4965_SENSITIVITY
INIT_WORK(&priv->sensitivity_work, iwl4965_bg_sensitivity_work);
#endif
-#ifdef CONFIG_IWL4965_HT
-#ifdef CONFIG_IWL4965_HT_AGG
- INIT_WORK(&priv->agg_work, iwl4965_bg_agg_work);
-#endif /* CONFIG_IWL4965_HT_AGG */
-#endif /* CONFIG_IWL4965_HT */
init_timer(&priv->statistics_periodic);
priv->statistics_periodic.data = (unsigned long)priv;
priv->statistics_periodic.function = iwl4965_bg_statistics_periodic;
Index: wl2_6_24_rc8_ev/drivers/net/wireless/iwlwifi/iwl-4965.h
===================================================================
--- wl2_6_24_rc8_ev.orig/drivers/net/wireless/iwlwifi/iwl-4965.h
+++ wl2_6_24_rc8_ev/drivers/net/wireless/iwlwifi/iwl-4965.h
@@ -782,11 +782,6 @@ extern int iwl4965_mac_ampdu_action(stru
const u8 *addr, u16 tid, u16 *ssn);
extern int iwl4965_check_empty_hw_queue(struct iwl4965_priv *priv, int sta_id,
u8 tid, int txq_id);
-#ifdef CONFIG_IWL4965_HT_AGG
-extern void iwl4965_turn_off_agg(struct iwl4965_priv *priv, u8 tid);
-extern void iwl4965_tl_get_stats(struct iwl4965_priv *priv,
- struct ieee80211_hdr *hdr);
-#endif /* CONFIG_IWL4965_HT_AGG */
#endif /*CONFIG_IWL4965_HT */
/* Structures, enum, and defines specific to the 4965 */

@@ -798,18 +793,6 @@ struct iwl4965_kw {
size_t size;
};

-#define TID_QUEUE_CELL_SPACING 50 /*mS */
-#define TID_QUEUE_MAX_SIZE 20
-#define TID_ROUND_VALUE 5 /* mS */
-#define TID_MAX_LOAD_COUNT 8
-
-#define TID_MAX_TIME_DIFF ((TID_QUEUE_MAX_SIZE - 1) * TID_QUEUE_CELL_SPACING)
-#define TIME_WRAP_AROUND(x, y) (((y) > (x)) ? (y) - (x) : (0-(x)) + (y))
-
-#define TID_ALL_ENABLED 0x7f
-#define TID_ALL_SPECIFIED 0xff
-#define TID_AGG_TPT_THREHOLD 0x0
-
#define IWL_CHANNEL_WIDTH_20MHZ 0
#define IWL_CHANNEL_WIDTH_40MHZ 1

@@ -834,37 +817,7 @@ struct iwl4965_kw {

#define TX_POWER_IWL_ILLEGAL_VOLTAGE -10000

-struct iwl4965_traffic_load {
- unsigned long time_stamp;
- u32 packet_count[TID_QUEUE_MAX_SIZE];
- u8 queue_count;
- u8 head;
- u32 total;
-};
-
-#ifdef CONFIG_IWL4965_HT_AGG
-/**
- * struct iwl4965_agg_control
- * @requested_ba: bit map of tids requesting aggregation/block-ack
- * @granted_ba: bit map of tids granted aggregation/block-ack
- */
-struct iwl4965_agg_control {
- unsigned long next_retry;
- u32 wait_for_agg_status;
- u32 tid_retry;
- u32 requested_ba;
- u32 granted_ba;
- u8 auto_agg;
- u32 tid_traffic_load_threshold;
- u32 ba_timeout;
- struct iwl4965_traffic_load traffic_load[TID_MAX_LOAD_COUNT];
-};
-#endif /*CONFIG_IWL4965_HT_AGG */
-
struct iwl4965_lq_mngr {
-#ifdef CONFIG_IWL4965_HT_AGG
- struct iwl4965_agg_control agg_ctrl;
-#endif
spinlock_t lock;
s32 max_window_size;
s32 *expected_tpt;
@@ -877,7 +830,6 @@ struct iwl4965_lq_mngr {
u8 lq_ready;
};

-
/* Sensitivity and chain noise calibration */
#define INTERFERENCE_DATA_AVAILABLE __constant_cpu_to_le32(1)
#define INITIALIZATION_VALUE 0xFFFF
@@ -1265,11 +1217,7 @@ struct iwl4965_priv {
#endif
struct work_struct statistics_work;
struct timer_list statistics_periodic;
-
-#ifdef CONFIG_IWL4965_HT_AGG
- struct work_struct agg_work;
-#endif
-}; /*iwl4965_priv */
+}; /*iwl4965_priv */

static inline int iwl4965_is_associated(struct iwl4965_priv *priv)
{
Index: wl2_6_24_rc8_ev/drivers/net/wireless/iwlwifi/iwl4965-base.c
===================================================================
--- wl2_6_24_rc8_ev.orig/drivers/net/wireless/iwlwifi/iwl4965-base.c
+++ wl2_6_24_rc8_ev/drivers/net/wireless/iwlwifi/iwl4965-base.c
@@ -3070,14 +3070,6 @@ static int iwl4965_tx_skb(struct iwl4965
out_cmd->cmd.tx.dram_lsb_ptr = cpu_to_le32(scratch_phys);
out_cmd->cmd.tx.dram_msb_ptr = iwl_get_dma_hi_address(scratch_phys);

-#ifdef CONFIG_IWL4965_HT_AGG
-#ifdef CONFIG_IWL4965_HT
- /* TODO: move this functionality to rate scaling */
- iwl4965_tl_get_stats(priv, hdr);
-#endif /* CONFIG_IWL4965_HT_AGG */
-#endif /*CONFIG_IWL4965_HT */
-
-
if (!ieee80211_get_morefrag(hdr)) {
txq->need_update = 1;
if (qc) {
@@ -8093,18 +8085,6 @@ static void iwl4965_mac_reset_tsf(struct
spin_lock_irqsave(&priv->lock, flags);
memset(&priv->current_ht_config, 0, sizeof(struct iwl_ht_info));
spin_unlock_irqrestore(&priv->lock, flags);
-#ifdef CONFIG_IWL4965_HT_AGG
-/* if (priv->lq_mngr.agg_ctrl.granted_ba)
- iwl4965_turn_off_agg(priv, TID_ALL_SPECIFIED);*/
-
- memset(&(priv->lq_mngr.agg_ctrl), 0, sizeof(struct iwl4965_agg_control));
- priv->lq_mngr.agg_ctrl.tid_traffic_load_threshold = 10;
- priv->lq_mngr.agg_ctrl.ba_timeout = 5000;
- priv->lq_mngr.agg_ctrl.auto_agg = 1;
-
- if (priv->lq_mngr.agg_ctrl.auto_agg)
- priv->lq_mngr.agg_ctrl.requested_ba = TID_ALL_ENABLED;
-#endif /*CONFIG_IWL4965_HT_AGG */
#endif /* CONFIG_IWL4965_HT */

#ifdef CONFIG_IWL4965_QOS

2008-01-28 12:07:40

by Ron Rindjunsky

[permalink] [raw]
Subject: [PATCH 10/12 v2] iwlwifi: A-MPDU Tx conform flows to mac80211

This patch alters the current iwlwifi behavior to fit the flows introduced
by the mac80211, mainly queues handling and start/stop call backs flows

Signed-off-by: Ron Rindjunsky <[email protected]>
---
drivers/net/wireless/iwlwifi/iwl-4965-commands.h | 24 ++-
drivers/net/wireless/iwlwifi/iwl-4965.c | 358 ++++++++++++----------
drivers/net/wireless/iwlwifi/iwl-4965.h | 20 +-
drivers/net/wireless/iwlwifi/iwl4965-base.c | 121 ++++----
4 files changed, 302 insertions(+), 221 deletions(-)

Index: wl2_6_24_rc8_ev/drivers/net/wireless/iwlwifi/iwl-4965-commands.h
===================================================================
--- wl2_6_24_rc8_ev.orig/drivers/net/wireless/iwlwifi/iwl-4965-commands.h
+++ wl2_6_24_rc8_ev/drivers/net/wireless/iwlwifi/iwl-4965-commands.h
@@ -1300,6 +1300,25 @@ struct iwl4965_tx_resp {
__le32 status; /* TX status (for aggregation status of 1st frame) */
} __attribute__ ((packed));

+struct agg_tx_status {
+ __le16 status;
+ __le16 sequence;
+} __attribute__ ((packed));
+
+struct iwl4965_tx_resp_agg {
+ u8 frame_count; /* 1 no aggregation, >1 aggregation */
+ u8 reserved1;
+ u8 failure_rts;
+ u8 failure_frame;
+ __le32 rate_n_flags;
+ __le16 wireless_media_time;
+ __le16 reserved3;
+ __le32 pa_power1;
+ __le32 pa_power2;
+ struct agg_tx_status status; /* TX status (for aggregation status */
+ /* of 1st frame) */
+} __attribute__ ((packed));
+
/*
* REPLY_COMPRESSED_BA = 0xc5 (response only, not a command)
*
@@ -1313,9 +1332,8 @@ struct iwl4965_compressed_ba_resp {
/* Index of recipient (BA-sending) station in uCode's station table */
u8 sta_id;
u8 tid;
- __le16 ba_seq_ctl;
- __le32 ba_bitmap0;
- __le32 ba_bitmap1;
+ __le16 seq_ctl;
+ __le64 bitmap;
__le16 scd_flow;
__le16 scd_ssn;
} __attribute__ ((packed));
Index: wl2_6_24_rc8_ev/drivers/net/wireless/iwlwifi/iwl-4965.c
===================================================================
--- wl2_6_24_rc8_ev.orig/drivers/net/wireless/iwlwifi/iwl-4965.c
+++ wl2_6_24_rc8_ev/drivers/net/wireless/iwlwifi/iwl-4965.c
@@ -79,6 +79,30 @@ const struct iwl4965_rate_info iwl4965_r
IWL_DECLARE_RATE_INFO(60, 60, 48, INV, 48, INV, 48, INV),/* 60mbps */
};

+#ifdef CONFIG_IWL4965_HT
+
+static const u16 default_tid_to_tx_fifo[] = {
+ IWL_TX_FIFO_AC1,
+ IWL_TX_FIFO_AC0,
+ IWL_TX_FIFO_AC0,
+ IWL_TX_FIFO_AC1,
+ IWL_TX_FIFO_AC2,
+ IWL_TX_FIFO_AC2,
+ IWL_TX_FIFO_AC3,
+ IWL_TX_FIFO_AC3,
+ IWL_TX_FIFO_NONE,
+ IWL_TX_FIFO_NONE,
+ IWL_TX_FIFO_NONE,
+ IWL_TX_FIFO_NONE,
+ IWL_TX_FIFO_NONE,
+ IWL_TX_FIFO_NONE,
+ IWL_TX_FIFO_NONE,
+ IWL_TX_FIFO_NONE,
+ IWL_TX_FIFO_AC3
+};
+
+#endif /*CONFIG_IWL4965_HT */
+
static int is_fat_channel(__le32 rxon_flags)
{
return (rxon_flags & RXON_FLG_CHANNEL_MODE_PURE_40_MSK) ||
@@ -4185,6 +4209,7 @@ static void iwl4965_set_tx_status(struct
tx_status->control.tx_rate = rate;
}

+#endif/* CONFIG_IWL4965_HT_AGG */

/**
* iwl4965_sta_modify_enable_tid_tx - Enable Tx for this TID in station table
@@ -4204,7 +4229,6 @@ static void iwl4965_sta_modify_enable_ti
iwl4965_send_add_station(priv, &priv->stations[sta_id].sta, CMD_ASYNC);
}

-
/**
* iwl4965_tx_status_reply_compressed_ba - Update tx status from block-ack
*
@@ -4218,10 +4242,11 @@ static int iwl4965_tx_status_reply_compr

{
int i, sh, ack;
- u16 ba_seq_ctl = le16_to_cpu(ba_resp->ba_seq_ctl);
- u32 bitmap0, bitmap1;
- u32 resp_bitmap0 = le32_to_cpu(ba_resp->ba_bitmap0);
- u32 resp_bitmap1 = le32_to_cpu(ba_resp->ba_bitmap1);
+ u16 seq_ctl = le16_to_cpu(ba_resp->seq_ctl);
+ u16 scd_flow = le16_to_cpu(ba_resp->scd_flow);
+ u64 bitmap;
+ int successes = 0;
+ struct ieee80211_tx_status *tx_status;

if (unlikely(!agg->wait_for_ba)) {
IWL_ERROR("Received BA when not expected\n");
@@ -4230,17 +4255,15 @@ static int iwl4965_tx_status_reply_compr

/* Mark that the expected block-ack response arrived */
agg->wait_for_ba = 0;
- IWL_DEBUG_TX_REPLY("BA %d %d\n", agg->start_idx, ba_resp->ba_seq_ctl);
+ IWL_DEBUG_TX_REPLY("BA %d %d\n", agg->start_idx, ba_resp->seq_ctl);

/* Calculate shift to align block-ack bits with our Tx window bits */
- sh = agg->start_idx - SEQ_TO_INDEX(ba_seq_ctl >> 4);
+ sh = agg->start_idx - SEQ_TO_INDEX(seq_ctl>>4);
if (sh < 0) /* tbw something is wrong with indices */
sh += 0x100;

/* don't use 64-bit values for now */
- bitmap0 = resp_bitmap0 >> sh;
- bitmap1 = resp_bitmap1 >> sh;
- bitmap0 |= (resp_bitmap1 & ((1 << sh) | ((1 << sh) - 1))) << (32 - sh);
+ bitmap = le64_to_cpu(ba_resp->bitmap) >> sh;

if (agg->frame_count > (64 - sh)) {
IWL_DEBUG_TX_REPLY("more frames than bitmap size");
@@ -4249,23 +4272,106 @@ static int iwl4965_tx_status_reply_compr

/* check for success or failure according to the
* transmitted bitmap and block-ack bitmap */
- bitmap0 &= agg->bitmap0;
- bitmap1 &= agg->bitmap1;
+ bitmap &= agg->bitmap;

/* For each frame attempted in aggregation,
* update driver's record of tx frame's status. */
for (i = 0; i < agg->frame_count ; i++) {
- int idx = (agg->start_idx + i) & 0xff;
- ack = bitmap0 & (1 << i);
+ ack = bitmap & (1 << i);
+ successes += !!ack;
IWL_DEBUG_TX_REPLY("%s ON i=%d idx=%d raw=%d\n",
- ack? "ACK":"NACK", i, idx, agg->start_idx + i);
- iwl4965_set_tx_status(priv, agg->txq_id, idx, ack, 0,
- agg->rate_n_flags);
+ ack? "ACK":"NACK", i, (agg->start_idx + i) & 0xff,
+ agg->start_idx + i);
+ }
+
+ tx_status = &priv->txq[scd_flow].txb[agg->start_idx].status;
+ tx_status->flags = IEEE80211_TX_STATUS_ACK;
+ tx_status->retry_count++;
+#ifdef CONFIG_IWL4965_HT_AGG
+ tx_status->flags |= IEEE80211_TX_STATUS_AGG_STATS;
+ tx_status->successes = successes;
+ tx_status->frame_count = agg->frame_count;
+#endif /* CONFIG_IWL4965_HT_AGG */
+ tx_status->control.tx_rate = agg->rate_n_flags;
+
+ IWL_DEBUG_TX_REPLY("Bitmap %llx\n", bitmap);
+
+ return 0;
+}
+
+/**
+ * iwl4965_tx_queue_stop_scheduler - Stop queue, but keep configuration
+ */
+static void iwl4965_tx_queue_stop_scheduler(struct iwl4965_priv *priv,
+ u16 txq_id)
+{
+ /* Simply stop the queue, but don't change any configuration;
+ * the SCD_ACT_EN bit is the write-enable mask for the ACTIVE bit. */
+ iwl4965_write_prph(priv,
+ KDR_SCD_QUEUE_STATUS_BITS(txq_id),
+ (0 << SCD_QUEUE_STTS_REG_POS_ACTIVE)|
+ (1 << SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN));
+}

+/**
+ * txq_id must be greater than IWL_BACK_QUEUE_FIRST_ID
+ */
+static int iwl4965_tx_queue_agg_disable(struct iwl4965_priv *priv, u16 txq_id,
+ u16 ssn_idx, u8 tx_fifo)
+{
+ if (IWL_BACK_QUEUE_FIRST_ID > txq_id) {
+ IWL_WARNING("queue number too small: %d, must be > %d\n",
+ txq_id, IWL_BACK_QUEUE_FIRST_ID);
+ return -EINVAL;
}

- IWL_DEBUG_TX_REPLY("Bitmap %x%x\n", bitmap0, bitmap1);
+ iwl4965_tx_queue_stop_scheduler(priv, txq_id);
+
+ iwl4965_clear_bits_prph(priv, KDR_SCD_QUEUECHAIN_SEL, (1 << txq_id));
+
+ priv->txq[txq_id].q.read_ptr = (ssn_idx & 0xff);
+ priv->txq[txq_id].q.write_ptr = (ssn_idx & 0xff);
+ /* supposes that ssn_idx is valid (!= 0xFFF) */
+ iwl4965_set_wr_ptrs(priv, txq_id, ssn_idx);
+
+ iwl4965_clear_bits_prph(priv, KDR_SCD_INTERRUPT_MASK, (1 << txq_id));
+ iwl4965_txq_ctx_deactivate(priv, txq_id);
+ iwl4965_tx_queue_set_status(priv, &priv->txq[txq_id], tx_fifo, 0);
+
+ return 0;
+}

+int iwl4965_check_empty_hw_queue(struct iwl4965_priv *priv, int sta_id,
+ u8 tid, int txq_id)
+{
+ struct iwl4965_queue *q = &priv->txq[txq_id].q;
+ u8 *addr = priv->stations[sta_id].sta.sta.addr;
+ struct iwl4965_tid_data *tid_data = &priv->stations[sta_id].tid[tid];
+
+ switch (priv->stations[sta_id].tid[tid].agg.state) {
+ case IWL_EMPTYING_HW_QUEUE_DELBA:
+ /* We are reclaiming the last packet of the */
+ /* aggregated HW queue */
+ if (txq_id == tid_data->agg.txq_id &&
+ q->read_ptr == q->write_ptr) {
+ u16 ssn = SEQ_TO_SN(tid_data->seq_number);
+ int tx_fifo = default_tid_to_tx_fifo[tid];
+ IWL_DEBUG_HT("HW queue empty: continue DELBA flow\n");
+ iwl4965_tx_queue_agg_disable(priv, txq_id,
+ ssn, tx_fifo);
+ tid_data->agg.state = IWL_AGG_OFF;
+ ieee80211_stop_tx_ba_cb_irqsafe(priv->hw, addr, tid);
+ }
+ break;
+ case IWL_EMPTYING_HW_QUEUE_ADDBA:
+ /* We are reclaiming the last packet of the queue */
+ if (tid_data->tfds_in_queue == 0) {
+ IWL_DEBUG_HT("HW queue empty: continue ADDBA flow\n");
+ tid_data->agg.state = IWL_AGG_ON;
+ ieee80211_start_tx_ba_cb_irqsafe(priv->hw, addr, tid);
+ }
+ break;
+ }
return 0;
}

@@ -4293,48 +4399,43 @@ static void iwl4965_rx_reply_compressed_
int index;
struct iwl4965_tx_queue *txq = NULL;
struct iwl4965_ht_agg *agg;
+ DECLARE_MAC_BUF(mac);

/* "flow" corresponds to Tx queue */
- u16 ba_resp_scd_flow = le16_to_cpu(ba_resp->scd_flow);
+ u16 scd_flow = le16_to_cpu(ba_resp->scd_flow);

/* "ssn" is start of block-ack Tx window, corresponds to index
* (in Tx queue's circular buffer) of first TFD/frame in window */
u16 ba_resp_scd_ssn = le16_to_cpu(ba_resp->scd_ssn);

- if (ba_resp_scd_flow >= ARRAY_SIZE(priv->txq)) {
+ if (scd_flow >= ARRAY_SIZE(priv->txq)) {
IWL_ERROR("BUG_ON scd_flow is bigger than number of queues");
return;
}

- txq = &priv->txq[ba_resp_scd_flow];
+ txq = &priv->txq[scd_flow];
agg = &priv->stations[ba_resp->sta_id].tid[ba_resp->tid].agg;

/* Find index just before block-ack window */
index = iwl4965_queue_dec_wrap(ba_resp_scd_ssn & 0xff, txq->q.n_bd);

/* TODO: Need to get this copy more safely - now good for debug */
-/*
- {
- DECLARE_MAC_BUF(mac);
+
IWL_DEBUG_TX_REPLY("REPLY_COMPRESSED_BA [%d]Received from %s, "
"sta_id = %d\n",
agg->wait_for_ba,
print_mac(mac, (u8*) &ba_resp->sta_addr_lo32),
ba_resp->sta_id);
- IWL_DEBUG_TX_REPLY("TID = %d, SeqCtl = %d, bitmap = 0x%X%X, scd_flow = "
+ IWL_DEBUG_TX_REPLY("TID = %d, SeqCtl = %d, bitmap = 0x%llx, scd_flow = "
"%d, scd_ssn = %d\n",
ba_resp->tid,
- ba_resp->ba_seq_ctl,
- ba_resp->ba_bitmap1,
- ba_resp->ba_bitmap0,
+ ba_resp->seq_ctl,
+ ba_resp->bitmap,
ba_resp->scd_flow,
ba_resp->scd_ssn);
- IWL_DEBUG_TX_REPLY("DAT start_idx = %d, bitmap = 0x%X%X \n",
+ IWL_DEBUG_TX_REPLY("DAT start_idx = %d, bitmap = 0x%llx \n",
agg->start_idx,
- agg->bitmap1,
- agg->bitmap0);
- }
-*/
+ agg->bitmap);

/* Update driver's record of ACK vs. not for each frame in window */
iwl4965_tx_status_reply_compressed_ba(priv, agg, ba_resp);
@@ -4342,23 +4443,17 @@ static void iwl4965_rx_reply_compressed_
/* Release all TFDs before the SSN, i.e. all TFDs in front of
* block-ack window (we assume that they've been successfully
* transmitted ... if not, it's too late anyway). */
- if (txq->q.read_ptr != (ba_resp_scd_ssn & 0xff))
- iwl4965_tx_queue_reclaim(priv, ba_resp_scd_flow, index);
-
-}
-
-
-/**
- * iwl4965_tx_queue_stop_scheduler - Stop queue, but keep configuration
- */
-static void iwl4965_tx_queue_stop_scheduler(struct iwl4965_priv *priv, u16 txq_id)
-{
- /* Simply stop the queue, but don't change any configuration;
- * the SCD_ACT_EN bit is the write-enable mask for the ACTIVE bit. */
- iwl4965_write_prph(priv,
- KDR_SCD_QUEUE_STATUS_BITS(txq_id),
- (0 << SCD_QUEUE_STTS_REG_POS_ACTIVE)|
- (1 << SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN));
+ if (txq->q.read_ptr != (ba_resp_scd_ssn & 0xff)) {
+ int freed = iwl4965_tx_queue_reclaim(priv, scd_flow, index);
+ priv->stations[ba_resp->sta_id].
+ tid[ba_resp->tid].tfds_in_queue -= freed;
+ if (iwl4965_queue_space(&txq->q) > txq->q.low_mark &&
+ priv->mac80211_registered &&
+ agg->state != IWL_EMPTYING_HW_QUEUE_DELBA)
+ ieee80211_wake_queue(priv->hw, scd_flow);
+ iwl4965_check_empty_hw_queue(priv, ba_resp->sta_id,
+ ba_resp->tid, scd_flow);
+ }
}

/**
@@ -4388,6 +4483,7 @@ static int iwl4965_tx_queue_set_q2ratid(
return 0;
}

+
/**
* iwl4965_tx_queue_agg_enable - Set up & enable aggregation for selected queue
*
@@ -4455,48 +4551,6 @@ static int iwl4965_tx_queue_agg_enable(s
return 0;
}

-/**
- * txq_id must be greater than IWL_BACK_QUEUE_FIRST_ID
- */
-static int iwl4965_tx_queue_agg_disable(struct iwl4965_priv *priv, u16 txq_id,
- u16 ssn_idx, u8 tx_fifo)
-{
- unsigned long flags;
- int rc;
-
- if (IWL_BACK_QUEUE_FIRST_ID > txq_id) {
- IWL_WARNING("queue number too small: %d, must be > %d\n",
- txq_id, IWL_BACK_QUEUE_FIRST_ID);
- return -EINVAL;
- }
-
- spin_lock_irqsave(&priv->lock, flags);
- rc = iwl4965_grab_nic_access(priv);
- if (rc) {
- spin_unlock_irqrestore(&priv->lock, flags);
- return rc;
- }
-
- iwl4965_tx_queue_stop_scheduler(priv, txq_id);
-
- iwl4965_clear_bits_prph(priv, KDR_SCD_QUEUECHAIN_SEL, (1 << txq_id));
-
- priv->txq[txq_id].q.read_ptr = (ssn_idx & 0xff);
- priv->txq[txq_id].q.write_ptr = (ssn_idx & 0xff);
- /* supposes that ssn_idx is valid (!= 0xFFF) */
- iwl4965_set_wr_ptrs(priv, txq_id, ssn_idx);
-
- iwl4965_clear_bits_prph(priv, KDR_SCD_INTERRUPT_MASK, (1 << txq_id));
- iwl4965_txq_ctx_deactivate(priv, txq_id);
- iwl4965_tx_queue_set_status(priv, &priv->txq[txq_id], tx_fifo, 0);
-
- iwl4965_release_nic_access(priv);
- spin_unlock_irqrestore(&priv->lock, flags);
-
- return 0;
-}
-
-#endif/* CONFIG_IWL4965_HT_AGG */
#endif /* CONFIG_IWL4965_HT */

/**
@@ -4717,28 +4771,6 @@ static void iwl4965_sta_modify_del_ba_ti
iwl4965_send_add_station(priv, &priv->stations[sta_id].sta, CMD_ASYNC);
}

-#ifdef CONFIG_IWL4965_HT_AGG
-
-static const u16 default_tid_to_tx_fifo[] = {
- IWL_TX_FIFO_AC1,
- IWL_TX_FIFO_AC0,
- IWL_TX_FIFO_AC0,
- IWL_TX_FIFO_AC1,
- IWL_TX_FIFO_AC2,
- IWL_TX_FIFO_AC2,
- IWL_TX_FIFO_AC3,
- IWL_TX_FIFO_AC3,
- IWL_TX_FIFO_NONE,
- IWL_TX_FIFO_NONE,
- IWL_TX_FIFO_NONE,
- IWL_TX_FIFO_NONE,
- IWL_TX_FIFO_NONE,
- IWL_TX_FIFO_NONE,
- IWL_TX_FIFO_NONE,
- IWL_TX_FIFO_NONE,
- IWL_TX_FIFO_AC3
-};
-
/*
* Find first available (lowest unused) Tx Queue, mark it "active".
* Called only when finding queue for aggregation.
@@ -4755,69 +4787,78 @@ static int iwl4965_txq_ctx_activate_free
return -1;
}

-int iwl4965_mac_ht_tx_agg_start(struct ieee80211_hw *hw, u8 *da, u16 tid,
- u16 *start_seq_num)
+static int iwl4965_mac_ht_tx_agg_start(struct ieee80211_hw *hw, const u8 *da,
+ u16 tid, u16 *start_seq_num)
{
-
struct iwl4965_priv *priv = hw->priv;
int sta_id;
int tx_fifo;
int txq_id;
int ssn = -1;
+ int rc = 0;
unsigned long flags;
struct iwl4965_tid_data *tid_data;
DECLARE_MAC_BUF(mac);

- /* Determine Tx DMA/FIFO channel for this Traffic ID */
if (likely(tid < ARRAY_SIZE(default_tid_to_tx_fifo)))
tx_fifo = default_tid_to_tx_fifo[tid];
else
return -EINVAL;

- IWL_WARNING("iwl-AGG iwl4965_mac_ht_tx_agg_start on da=%s"
- " tid=%d\n", print_mac(mac, da), tid);
+ IWL_WARNING("%s on da = %s tid = %d\n",
+ __func__, print_mac(mac, da), tid);

- /* Get index into station table */
sta_id = iwl4965_hw_find_station(priv, da);
if (sta_id == IWL_INVALID_STATION)
return -ENXIO;

- /* Find available Tx queue for aggregation */
+ if (priv->stations[sta_id].tid[tid].agg.state != IWL_AGG_OFF) {
+ IWL_ERROR("Start AGG when state is not IWL_AGG_OFF !\n");
+ return -ENXIO;
+ }
+
txq_id = iwl4965_txq_ctx_activate_free(priv);
if (txq_id == -1)
return -ENXIO;

spin_lock_irqsave(&priv->sta_lock, flags);
tid_data = &priv->stations[sta_id].tid[tid];
-
- /* Get starting sequence number for 1st frame in block ack window.
- * We'll use least signif byte as 1st frame's index into Tx queue. */
ssn = SEQ_TO_SN(tid_data->seq_number);
tid_data->agg.txq_id = txq_id;
spin_unlock_irqrestore(&priv->sta_lock, flags);

*start_seq_num = ssn;
-
- /* Update driver's link quality manager */
- iwl4965_ba_status(priv, tid, BA_STATUS_ACTIVE);
-
- /* Set up and enable aggregation for selected Tx queue and FIFO */
- return iwl4965_tx_queue_agg_enable(priv, txq_id, tx_fifo,
+ rc = iwl4965_tx_queue_agg_enable(priv, txq_id, tx_fifo,
sta_id, tid, ssn);
-}
+ if (rc)
+ return rc;

+ rc = 0;
+ if (tid_data->tfds_in_queue == 0) {
+ printk(KERN_ERR "HW queue is empty\n");
+ tid_data->agg.state = IWL_AGG_ON;
+ ieee80211_start_tx_ba_cb_irqsafe(hw, da, tid);
+ } else {
+ IWL_DEBUG_HT("HW queue is NOT empty: %d packets in HW queue\n",
+ tid_data->tfds_in_queue);
+ tid_data->agg.state = IWL_EMPTYING_HW_QUEUE_ADDBA;
+ }
+ return rc;
+}

-int iwl4965_mac_ht_tx_agg_stop(struct ieee80211_hw *hw, u8 *da, u16 tid)
+static int iwl4965_mac_ht_tx_agg_stop(struct ieee80211_hw *hw, const u8 *da,
+ u16 tid)
{

struct iwl4965_priv *priv = hw->priv;
int tx_fifo_id, txq_id, sta_id, ssn = -1;
struct iwl4965_tid_data *tid_data;
- int rc;
+ int rc, write_ptr, read_ptr;
+ unsigned long flags;
DECLARE_MAC_BUF(mac);

if (!da) {
- IWL_ERROR("%s: da = NULL\n", __func__);
+ IWL_ERROR("da = NULL\n");
return -EINVAL;
}

@@ -4831,33 +4872,44 @@ int iwl4965_mac_ht_tx_agg_stop(struct ie
if (sta_id == IWL_INVALID_STATION)
return -ENXIO;

+ if (priv->stations[sta_id].tid[tid].agg.state != IWL_AGG_ON)
+ IWL_WARNING("Stopping AGG while state not IWL_AGG_ON\n");
+
tid_data = &priv->stations[sta_id].tid[tid];
ssn = (tid_data->seq_number & IEEE80211_SCTL_SEQ) >> 4;
txq_id = tid_data->agg.txq_id;
+ write_ptr = priv->txq[txq_id].q.write_ptr;
+ read_ptr = priv->txq[txq_id].q.read_ptr;

- rc = iwl4965_tx_queue_agg_disable(priv, txq_id, ssn, tx_fifo_id);
- /* FIXME: need more safe way to handle error condition */
- if (rc)
- return rc;
+ /* The queue is not empty */
+ if (write_ptr != read_ptr) {
+ IWL_DEBUG_HT("Stopping a non empty AGG HW QUEUE\n");
+ priv->stations[sta_id].tid[tid].agg.state =
+ IWL_EMPTYING_HW_QUEUE_DELBA;
+ return 0;
+ }

- iwl4965_ba_status(priv, tid, BA_STATUS_INITIATOR_DELBA);
- IWL_DEBUG_INFO("iwl4965_mac_ht_tx_agg_stop on da=%s tid=%d\n",
- print_mac(mac, da), tid);
+ IWL_DEBUG_HT("HW queue empty\n");;
+ priv->stations[sta_id].tid[tid].agg.state = IWL_AGG_OFF;

- return 0;
-}
+ spin_lock_irqsave(&priv->lock, flags);
+ rc = iwl4965_grab_nic_access(priv);
+ if (rc) {
+ spin_unlock_irqrestore(&priv->lock, flags);
+ return rc;
+ }
+ rc = iwl4965_tx_queue_agg_disable(priv, txq_id, ssn, tx_fifo_id);
+ iwl4965_release_nic_access(priv);
+ spin_unlock_irqrestore(&priv->lock, flags);

+ if (rc)
+ return rc;

-#endif /* CONFIG_IWL4965_HT_AGG */
+ ieee80211_stop_tx_ba_cb_irqsafe(priv->hw, da, tid);

-int iwl4965_mac_ht_tx_agg_start(struct ieee80211_hw *hw, const u8 *da, u16 tid,
- u16 *start_seq_num)
-{
- return 0;
-}
+ IWL_DEBUG_INFO("iwl4965_mac_ht_tx_agg_stop on da=%s tid=%d\n",
+ print_mac(mac, da), tid);

-int iwl4965_mac_ht_tx_agg_stop(struct ieee80211_hw *hw, const u8 *da, u16 tid)
-{
return 0;
}

@@ -4911,9 +4963,7 @@ void iwl4965_hw_rx_handler_setup(struct
iwl4965_rx_missed_beacon_notif;

#ifdef CONFIG_IWL4965_HT
-#ifdef CONFIG_IWL4965_HT_AGG
priv->rx_handlers[REPLY_COMPRESSED_BA] = iwl4965_rx_reply_compressed_ba;
-#endif /* CONFIG_IWL4965_HT_AGG */
#endif /* CONFIG_IWL4965_HT */
}

Index: wl2_6_24_rc8_ev/drivers/net/wireless/iwlwifi/iwl-4965.h
===================================================================
--- wl2_6_24_rc8_ev.orig/drivers/net/wireless/iwlwifi/iwl-4965.h
+++ wl2_6_24_rc8_ev/drivers/net/wireless/iwlwifi/iwl-4965.h
@@ -433,7 +433,6 @@ struct iwl4965_rx_queue {
#define IWL_INVALID_VALUE -1

#ifdef CONFIG_IWL4965_HT
-#ifdef CONFIG_IWL4965_HT_AGG
/**
* struct iwl4965_ht_agg -- aggregation status while waiting for block-ack
* @txq_id: Tx queue used for Tx attempt
@@ -453,19 +452,22 @@ struct iwl4965_ht_agg {
u16 frame_count;
u16 wait_for_ba;
u16 start_idx;
- u32 bitmap0;
- u32 bitmap1;
+ u64 bitmap;
u32 rate_n_flags;
+#define IWL_AGG_OFF 0
+#define IWL_AGG_ON 1
+#define IWL_EMPTYING_HW_QUEUE_ADDBA 2
+#define IWL_EMPTYING_HW_QUEUE_DELBA 3
+ u8 state;
};
-#endif /* CONFIG_IWL4965_HT_AGG */
+
#endif /* CONFIG_IWL4965_HT */

struct iwl4965_tid_data {
u16 seq_number;
+ u16 tfds_in_queue;
#ifdef CONFIG_IWL4965_HT
-#ifdef CONFIG_IWL4965_HT_AGG
struct iwl4965_ht_agg agg;
-#endif /* CONFIG_IWL4965_HT_AGG */
#endif /* CONFIG_IWL4965_HT */
};

@@ -743,7 +745,7 @@ extern u8 iwl4965_hw_find_station(struct

extern int iwl4965_hw_channel_switch(struct iwl4965_priv *priv, u16 channel);
extern int iwl4965_tx_queue_reclaim(struct iwl4965_priv *priv, int txq_id, int index);
-
+extern int iwl4965_queue_space(const struct iwl4965_queue *q);
struct iwl4965_priv;

/*
@@ -778,6 +780,8 @@ extern void iwl4965_set_ht_add_station(s
extern int iwl4965_mac_ampdu_action(struct ieee80211_hw *hw,
enum ieee80211_ampdu_mlme_action action,
const u8 *addr, u16 tid, u16 *ssn);
+extern int iwl4965_check_empty_hw_queue(struct iwl4965_priv *priv, int sta_id,
+ u8 tid, int txq_id);
#ifdef CONFIG_IWL4965_HT_AGG
extern void iwl4965_turn_off_agg(struct iwl4965_priv *priv, u8 tid);
extern void iwl4965_tl_get_stats(struct iwl4965_priv *priv,
@@ -855,7 +859,7 @@ struct iwl4965_agg_control {
u32 ba_timeout;
struct iwl4965_traffic_load traffic_load[TID_MAX_LOAD_COUNT];
};
-#endif /*CONFIG_IWL4965_HT_AGG */
+#endif /*CONFIG_IWL4965_HT_AGG */

struct iwl4965_lq_mngr {
#ifdef CONFIG_IWL4965_HT_AGG
Index: wl2_6_24_rc8_ev/drivers/net/wireless/iwlwifi/iwl4965-base.c
===================================================================
--- wl2_6_24_rc8_ev.orig/drivers/net/wireless/iwlwifi/iwl4965-base.c
+++ wl2_6_24_rc8_ev/drivers/net/wireless/iwlwifi/iwl4965-base.c
@@ -205,7 +205,7 @@ static void iwl4965_print_hex_dump(int l
* See more detailed info in iwl-4965-hw.h.
***************************************************/

-static int iwl4965_queue_space(const struct iwl4965_queue *q)
+int iwl4965_queue_space(const struct iwl4965_queue *q)
{
int s = q->read_ptr - q->write_ptr;

@@ -2967,11 +2967,10 @@ static int iwl4965_tx_skb(struct iwl4965
__constant_cpu_to_le16(IEEE80211_SCTL_FRAG));
seq_number += 0x10;
#ifdef CONFIG_IWL4965_HT
-#ifdef CONFIG_IWL4965_HT_AGG
/* aggregation is on for this <sta,tid> */
- if (ctl->flags & IEEE80211_TXCTL_HT_MPDU_AGG)
+ if (ctl->flags & IEEE80211_TXCTL_AMPDU)
txq_id = priv->stations[sta_id].tid[tid].agg.txq_id;
-#endif /* CONFIG_IWL4965_HT_AGG */
+ priv->stations[sta_id].tid[tid].tfds_in_queue++;
#endif /* CONFIG_IWL4965_HT */
}

@@ -3523,10 +3522,10 @@ int iwl4965_tx_queue_reclaim(struct iwl4
nfreed++;
}

- if (iwl4965_queue_space(q) > q->low_mark && (txq_id >= 0) &&
+/* if (iwl4965_queue_space(q) > q->low_mark && (txq_id >= 0) &&
(txq_id != IWL_CMD_QUEUE_NUM) &&
priv->mac80211_registered)
- ieee80211_wake_queue(priv->hw, txq_id);
+ ieee80211_wake_queue(priv->hw, txq_id); */


return nfreed;
@@ -3545,7 +3544,6 @@ static int iwl4965_is_tx_success(u32 sta
*
******************************************************************************/
#ifdef CONFIG_IWL4965_HT
-#ifdef CONFIG_IWL4965_HT_AGG

static inline int iwl4965_get_ra_sta_id(struct iwl4965_priv *priv,
struct ieee80211_hdr *hdr)
@@ -3580,11 +3578,11 @@ static inline u32 iwl4965_get_scd_ssn(st
*/
static int iwl4965_tx_status_reply_tx(struct iwl4965_priv *priv,
struct iwl4965_ht_agg *agg,
- struct iwl4965_tx_resp *tx_resp,
+ struct iwl4965_tx_resp_agg *tx_resp,
u16 start_idx)
{
- u32 status;
- __le32 *frame_status = &tx_resp->status;
+ u16 status;
+ struct agg_tx_status *frame_status = &tx_resp->status;
struct ieee80211_tx_status *tx_status = NULL;
struct ieee80211_hdr *hdr = NULL;
int i, sh;
@@ -3597,26 +3595,25 @@ static int iwl4965_tx_status_reply_tx(st
agg->frame_count = tx_resp->frame_count;
agg->start_idx = start_idx;
agg->rate_n_flags = le32_to_cpu(tx_resp->rate_n_flags);
- agg->bitmap0 = agg->bitmap1 = 0;
+ agg->bitmap = 0;

/* # frames attempted by Tx command */
if (agg->frame_count == 1) {
/* Only one frame was attempted; no block-ack will arrive */
- struct iwl4965_tx_queue *txq ;
- status = le32_to_cpu(frame_status[0]);
+ status = le16_to_cpu(frame_status[0].status);
+ seq = le16_to_cpu(frame_status[0].sequence);
+ idx = SEQ_TO_INDEX(seq);
+ txq_id = SEQ_TO_QUEUE(seq);

- txq_id = agg->txq_id;
- txq = &priv->txq[txq_id];
/* FIXME: code repetition */
- IWL_DEBUG_TX_REPLY("FrameCnt = %d, StartIdx=%d \n",
- agg->frame_count, agg->start_idx);
+ IWL_DEBUG_TX_REPLY("FrameCnt = %d, StartIdx=%d idx=%d\n",
+ agg->frame_count, agg->start_idx, idx);

- tx_status = &(priv->txq[txq_id].txb[txq->q.read_ptr].status);
+ tx_status = &(priv->txq[txq_id].txb[idx].status);
tx_status->retry_count = tx_resp->failure_frame;
tx_status->queue_number = status & 0xff;
- tx_status->queue_length = tx_resp->bt_kill_count;
- tx_status->queue_length |= tx_resp->failure_rts;
-
+ tx_status->queue_length = tx_resp->failure_rts;
+ tx_status->control.flags &= ~IEEE80211_TXCTL_AMPDU;
tx_status->flags = iwl4965_is_tx_success(status)?
IEEE80211_TX_STATUS_ACK : 0;
tx_status->control.tx_rate =
@@ -3637,8 +3634,8 @@ static int iwl4965_tx_status_reply_tx(st
/* Construct bit-map of pending frames within Tx window */
for (i = 0; i < agg->frame_count; i++) {
u16 sc;
- status = le32_to_cpu(frame_status[i]);
- seq = status >> 16;
+ status = le16_to_cpu(frame_status[i].status);
+ seq = le16_to_cpu(frame_status[i].sequence);
idx = SEQ_TO_INDEX(seq);
txq_id = SEQ_TO_QUEUE(seq);

@@ -3682,13 +3679,12 @@ static int iwl4965_tx_status_reply_tx(st
start, (u32)(bitmap & 0xFFFFFFFF));
}

- agg->bitmap0 = bitmap & 0xFFFFFFFF;
- agg->bitmap1 = bitmap >> 32;
+ agg->bitmap = bitmap;
agg->start_idx = start;
agg->rate_n_flags = le32_to_cpu(tx_resp->rate_n_flags);
- IWL_DEBUG_TX_REPLY("Frames %d start_idx=%d bitmap=0x%x\n",
+ IWL_DEBUG_TX_REPLY("Frames %d start_idx=%d bitmap=0x%llx\n",
agg->frame_count, agg->start_idx,
- agg->bitmap0);
+ agg->bitmap);

if (bitmap)
agg->wait_for_ba = 1;
@@ -3696,7 +3692,6 @@ static int iwl4965_tx_status_reply_tx(st
return 0;
}
#endif
-#endif

/**
* iwl4965_rx_reply_tx - Handle standard (non-aggregation) Tx response
@@ -3713,9 +3708,9 @@ static void iwl4965_rx_reply_tx(struct i
struct iwl4965_tx_resp *tx_resp = (void *)&pkt->u.raw[0];
u32 status = le32_to_cpu(tx_resp->status);
#ifdef CONFIG_IWL4965_HT
-#ifdef CONFIG_IWL4965_HT_AGG
- int tid, sta_id;
-#endif
+ int tid = MAX_TID_COUNT, sta_id = IWL_INVALID_STATION;
+ struct ieee80211_hdr *hdr;
+ __le16 *qc;
#endif

if ((index >= txq->q.n_bd) || (x2_queue_used(&txq->q, index) == 0)) {
@@ -3727,44 +3722,51 @@ static void iwl4965_rx_reply_tx(struct i
}

#ifdef CONFIG_IWL4965_HT
-#ifdef CONFIG_IWL4965_HT_AGG
+ hdr = iwl4965_tx_queue_get_hdr(priv, txq_id, index);
+ qc = ieee80211_get_qos_ctrl(hdr);
+
+ if (qc)
+ tid = le16_to_cpu(*qc) & 0xf;
+
+ sta_id = iwl4965_get_ra_sta_id(priv, hdr);
+ if (txq->sched_retry && unlikely(sta_id == IWL_INVALID_STATION)) {
+ IWL_ERROR("Station not known\n");
+ return;
+ }
+
if (txq->sched_retry) {
const u32 scd_ssn = iwl4965_get_scd_ssn(tx_resp);
- struct ieee80211_hdr *hdr =
- iwl4965_tx_queue_get_hdr(priv, txq_id, index);
struct iwl4965_ht_agg *agg = NULL;
- __le16 *qc = ieee80211_get_qos_ctrl(hdr);

- if (qc == NULL) {
- IWL_ERROR("BUG_ON qc is null!!!!\n");
+ if (!qc)
return;
- }
-
- tid = le16_to_cpu(*qc) & 0xf;
-
- sta_id = iwl4965_get_ra_sta_id(priv, hdr);
- if (unlikely(sta_id == IWL_INVALID_STATION)) {
- IWL_ERROR("Station not known for\n");
- return;
- }

agg = &priv->stations[sta_id].tid[tid].agg;

- iwl4965_tx_status_reply_tx(priv, agg, tx_resp, index);
+ iwl4965_tx_status_reply_tx(priv, agg,
+ (struct iwl4965_tx_resp_agg *)tx_resp, index);

if ((tx_resp->frame_count == 1) &&
!iwl4965_is_tx_success(status)) {
/* TODO: send BAR */
}

- if ((txq->q.read_ptr != (scd_ssn & 0xff))) {
+ if (txq->q.read_ptr != (scd_ssn & 0xff)) {
+ int freed;
index = iwl4965_queue_dec_wrap(scd_ssn & 0xff, txq->q.n_bd);
IWL_DEBUG_TX_REPLY("Retry scheduler reclaim scd_ssn "
"%d index %d\n", scd_ssn , index);
- iwl4965_tx_queue_reclaim(priv, txq_id, index);
+ freed = iwl4965_tx_queue_reclaim(priv, txq_id, index);
+ priv->stations[sta_id].tid[tid].tfds_in_queue -= freed;
+
+ if (iwl4965_queue_space(&txq->q) > txq->q.low_mark &&
+ txq_id >= 0 && priv->mac80211_registered &&
+ agg->state != IWL_EMPTYING_HW_QUEUE_DELBA)
+ ieee80211_wake_queue(priv->hw, txq_id);
+
+ iwl4965_check_empty_hw_queue(priv, sta_id, tid, txq_id);
}
} else {
-#endif /* CONFIG_IWL4965_HT_AGG */
#endif /* CONFIG_IWL4965_HT */
tx_status = &(txq->txb[txq->q.read_ptr].status);

@@ -3785,12 +3787,21 @@ static void iwl4965_rx_reply_tx(struct i
tx_resp->failure_frame);

IWL_DEBUG_TX_REPLY("Tx queue reclaim %d\n", index);
- if (index != -1)
- iwl4965_tx_queue_reclaim(priv, txq_id, index);
+ if (index != -1) {
+ int freed = iwl4965_tx_queue_reclaim(priv, txq_id, index);
+#ifdef CONFIG_IWL4965_HT
+ if (tid != MAX_TID_COUNT)
+ priv->stations[sta_id].tid[tid].tfds_in_queue -= freed;
+ if (iwl4965_queue_space(&txq->q) > txq->q.low_mark &&
+ (txq_id >= 0) &&
+ priv->mac80211_registered)
+ ieee80211_wake_queue(priv->hw, txq_id);
+ if (tid != MAX_TID_COUNT)
+ iwl4965_check_empty_hw_queue(priv, sta_id, tid, txq_id);
+#endif
+ }
#ifdef CONFIG_IWL4965_HT
-#ifdef CONFIG_IWL4965_HT_AGG
}
-#endif /* CONFIG_IWL4965_HT_AGG */
#endif /* CONFIG_IWL4965_HT */

if (iwl_check_bits(status, TX_ABORT_REQUIRED_MSK))
@@ -9080,10 +9091,8 @@ static int iwl4965_pci_probe(struct pci_
/* Default value; 4 EDCA QOS priorities */
hw->queues = 4;
#ifdef CONFIG_IWL4965_HT
-#ifdef CONFIG_IWL4965_HT_AGG
/* Enhanced value; more queues, to support 11n aggregation */
hw->queues = 16;
-#endif /* CONFIG_IWL4965_HT_AGG */
#endif /* CONFIG_IWL4965_HT */

spin_lock_init(&priv->lock);

2008-01-28 12:07:38

by Ron Rindjunsky

[permalink] [raw]
Subject: [PATCH 02/12 v2] mac80211: A-MPDU Tx add MLME structures

This patch adds the needed structures to describe the Tx aggregation MLME
per STA
new:
- struct tid_ampdu_tx: TID aggregation information (Tx)
changed:
- struct sta_ampdu_mlme: Tx aggregation information per TID and
dialog token creator were added
- struct sta_info: tid_to_tx_q added for tid<->tx queue mapping

Signed-off-by: Ron Rindjunsky <[email protected]>
---
net/mac80211/sta_info.h | 36 +++++++++++++++++++++++++++++++++---
1 files changed, 33 insertions(+), 3 deletions(-)

diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index 96fe3ed..48a620a 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -33,13 +33,36 @@

#define STA_TID_NUM 16
#define ADDBA_RESP_INTERVAL HZ
+#define HT_AGG_MAX_RETRIES (0x3)

#define HT_AGG_STATE_INITIATOR_SHIFT (4)

+#define HT_ADDBA_REQUESTED_MSK BIT(0)
+#define HT_ADDBA_DRV_READY_MSK BIT(1)
+#define HT_ADDBA_RECEIVED_MSK BIT(2)
#define HT_AGG_STATE_REQ_STOP_BA_MSK BIT(3)
-
+#define HT_AGG_STATE_INITIATOR_MSK BIT(HT_AGG_STATE_INITIATOR_SHIFT)
#define HT_AGG_STATE_IDLE (0x0)
-#define HT_AGG_STATE_OPERATIONAL (0x7)
+#define HT_AGG_STATE_OPERATIONAL (HT_ADDBA_REQUESTED_MSK | \
+ HT_ADDBA_DRV_READY_MSK | \
+ HT_ADDBA_RECEIVED_MSK)
+
+/**
+ * struct tid_ampdu_tx - TID aggregation information (Tx).
+ *
+ * @state: TID's state in session state machine.
+ * @dialog_token: dialog token for aggregation session
+ * @ssn: Starting Sequence Number expected to be aggregated.
+ * @addba_resp_timer: timer for peer's response to addba request
+ * @addba_req_num: number of times addBA request has been sent.
+ */
+struct tid_ampdu_tx {
+ u8 state;
+ u8 dialog_token;
+ u16 ssn;
+ struct timer_list addba_resp_timer;
+ u8 addba_req_num;
+};

/**
* struct tid_ampdu_rx - TID aggregation information (Rx).
@@ -69,12 +92,18 @@ struct tid_ampdu_rx {
/**
* struct sta_ampdu_mlme - STA aggregation information.
*
- * @tid_agg_info_rx: aggregation info for Rx per TID
+ * @tid_rx: aggregation info for Rx per TID
+ * @tid_tx: aggregation info for Tx per TID
* @ampdu_rx: for locking sections in aggregation Rx flow
+ * @ampdu_tx: for locking sectionsi in aggregation Tx flow
+ * @dialog_token_allocator: dialog token enumerator for each new session;
*/
struct sta_ampdu_mlme {
struct tid_ampdu_rx tid_rx[STA_TID_NUM];
+ struct tid_ampdu_tx tid_tx[STA_TID_NUM];
spinlock_t ampdu_rx;
+ spinlock_t ampdu_tx;
+ u8 dialog_token_allocator;
};

struct sta_info {
@@ -148,6 +177,7 @@ struct sta_info {
of this STA */
struct sta_ampdu_mlme ampdu_mlme;
u8 timer_to_tid[STA_TID_NUM]; /* convert timer id to tid */
+ u8 tid_to_tx_q[STA_TID_NUM]; /* map tid to tx queue */

#ifdef CONFIG_MAC80211_DEBUGFS
struct sta_info_debugfsdentries {
--
1.5.3.3


2008-01-28 12:07:43

by Ron Rindjunsky

[permalink] [raw]
Subject: [PATCH 11/12 v2] iwlwifi: A-MPDU Tx conform block Ack rate scaling to mac80211

This patch uses the changes in ieee80211_tx_status to pass Block Ack data
to rate scaling module, and uses this data in rate scaling calculations

Signed-off-by: Ron Rindjunsky <[email protected]>
---
drivers/net/wireless/iwlwifi/iwl-4965-rs.c | 85 +++++++++++++++++++---------
drivers/net/wireless/iwlwifi/iwl-4965.c | 9 +--
2 files changed, 60 insertions(+), 34 deletions(-)

diff --git a/drivers/net/wireless/iwlwifi/iwl-4965-rs.c b/drivers/net/wireless/iwlwifi/iwl-4965-rs.c
index d064622..f4f2497 100644
--- a/drivers/net/wireless/iwlwifi/iwl-4965-rs.c
+++ b/drivers/net/wireless/iwlwifi/iwl-4965-rs.c
@@ -277,7 +277,8 @@ static void rs_rate_scale_clear_window(struct iwl4965_rate_scale_data *window)
* packets.
*/
static int rs_collect_tx_data(struct iwl4965_rate_scale_data *windows,
- int scale_index, s32 tpt, u32 status)
+ int scale_index, s32 tpt, int retries,
+ int successes)
{
struct iwl4965_rate_scale_data *window = NULL;
u64 mask;
@@ -298,26 +299,33 @@ static int rs_collect_tx_data(struct iwl4965_rate_scale_data *windows,
* subtract "1" from the success counter (this is the main reason
* we keep these bitmaps!).
*/
- if (window->counter >= win_size) {
- window->counter = win_size - 1;
- mask = 1;
- mask = (mask << (win_size - 1));
- if ((window->data & mask)) {
- window->data &= ~mask;
- window->success_counter = window->success_counter - 1;
+ while (retries > 0) {
+ if (window->counter >= win_size) {
+ window->counter = win_size - 1;
+ mask = 1;
+ mask = (mask << (win_size - 1));
+ if (window->data & mask) {
+ window->data &= ~mask;
+ window->success_counter =
+ window->success_counter - 1;
+ }
}
- }

- /* Increment frames-attempted counter */
- window->counter = window->counter + 1;
+ /* Increment frames-attempted counter */
+ window->counter++;
+
+ /* Shift bitmap by one frame (throw away oldest history),
+ * OR in "1", and increment "success" if this
+ * frame was successful. */
+ mask = window->data;
+ window->data = (mask << 1);
+ if (successes > 0) {
+ window->success_counter = window->success_counter + 1;
+ window->data |= 0x1;
+ successes--;
+ }

- /* Shift bitmap by one frame (throw away oldest history),
- * OR in "1", and increment "success" if this frame was successful. */
- mask = window->data;
- window->data = (mask << 1);
- if (status != 0) {
- window->success_counter = window->success_counter + 1;
- window->data |= 0x1;
+ retries--;
}

/* Calculate current success ratio, avoid divide-by-0! */
@@ -677,6 +685,11 @@ static void rs_tx_status(void *priv_rate, struct net_device *dev,
if (!ieee80211_is_data(fc) || is_multicast_ether_addr(hdr->addr1))
return;

+ /* This packet was aggregated but doesn't carry rate scale info */
+ if ((tx_resp->control.flags & IEEE80211_TXCTL_AMPDU) &&
+ !(tx_resp->flags & IEEE80211_TX_STATUS_AMPDU))
+ return;
+
retries = tx_resp->retry_count;

if (retries > 15)
@@ -766,7 +779,7 @@ static void rs_tx_status(void *priv_rate, struct net_device *dev,
tpt = search_tbl->expected_tpt[rs_index];
else
tpt = 0;
- rs_collect_tx_data(search_win, rs_index, tpt, 0);
+ rs_collect_tx_data(search_win, rs_index, tpt, 1, 0);

/* Else if type matches "current/active" table,
* add failure to "current/active" history */
@@ -777,7 +790,7 @@ static void rs_tx_status(void *priv_rate, struct net_device *dev,
tpt = curr_tbl->expected_tpt[rs_index];
else
tpt = 0;
- rs_collect_tx_data(window, rs_index, tpt, 0);
+ rs_collect_tx_data(window, rs_index, tpt, 1, 0);
}

/* If not searching for a new mode, increment failed counter
@@ -818,9 +831,13 @@ static void rs_tx_status(void *priv_rate, struct net_device *dev,
tpt = search_tbl->expected_tpt[rs_index];
else
tpt = 0;
- rs_collect_tx_data(search_win,
- rs_index, tpt, status);
-
+ if (tx_resp->control.flags & IEEE80211_TXCTL_AMPDU)
+ rs_collect_tx_data(search_win, rs_index, tpt,
+ tx_resp->ampdu_ack_len,
+ tx_resp->ampdu_ack_map);
+ else
+ rs_collect_tx_data(search_win, rs_index, tpt,
+ 1, status);
/* Else if type matches "current/active" table,
* add final tx status to "current/active" history */
} else if ((tbl_type.lq_type == curr_tbl->lq_type) &&
@@ -830,16 +847,28 @@ static void rs_tx_status(void *priv_rate, struct net_device *dev,
tpt = curr_tbl->expected_tpt[rs_index];
else
tpt = 0;
- rs_collect_tx_data(window, rs_index, tpt, status);
+ if (tx_resp->control.flags & IEEE80211_TXCTL_AMPDU)
+ rs_collect_tx_data(window, rs_index, tpt,
+ tx_resp->ampdu_ack_len,
+ tx_resp->ampdu_ack_map);
+ else
+ rs_collect_tx_data(window, rs_index, tpt,
+ 1, status);
}

/* If not searching for new mode, increment success/failed counter
* ... these help determine when to start searching again */
if (lq_sta->stay_in_tbl) {
- if (status)
- lq_sta->total_success++;
- else
- lq_sta->total_failed++;
+ if (tx_resp->control.flags & IEEE80211_TXCTL_AMPDU) {
+ lq_sta->total_success += tx_resp->ampdu_ack_map;
+ lq_sta->total_failed +=
+ (tx_resp->ampdu_ack_len - tx_resp->ampdu_ack_map);
+ } else {
+ if (status)
+ lq_sta->total_success++;
+ else
+ lq_sta->total_failed++;
+ }
}

/* See if there's a better rate or modulation mode to try. */
diff --git a/drivers/net/wireless/iwlwifi/iwl-4965.c b/drivers/net/wireless/iwlwifi/iwl-4965.c
index 317512e..d5cd260 100644
--- a/drivers/net/wireless/iwlwifi/iwl-4965.c
+++ b/drivers/net/wireless/iwlwifi/iwl-4965.c
@@ -4286,12 +4286,9 @@ static int iwl4965_tx_status_reply_compressed_ba(struct iwl4965_priv *priv,

tx_status = &priv->txq[scd_flow].txb[agg->start_idx].status;
tx_status->flags = IEEE80211_TX_STATUS_ACK;
- tx_status->retry_count++;
-#ifdef CONFIG_IWL4965_HT_AGG
- tx_status->flags |= IEEE80211_TX_STATUS_AGG_STATS;
- tx_status->successes = successes;
- tx_status->frame_count = agg->frame_count;
-#endif /* CONFIG_IWL4965_HT_AGG */
+ tx_status->flags |= IEEE80211_TX_STATUS_AMPDU;
+ tx_status->ampdu_ack_map = successes;
+ tx_status->ampdu_ack_len = agg->frame_count;
tx_status->control.tx_rate = agg->rate_n_flags;

IWL_DEBUG_TX_REPLY("Bitmap %llx\n", bitmap);
--
1.5.3.3


2008-01-28 12:08:50

by Ron Rindjunsky

[permalink] [raw]
Subject: [PATCH 01/12 v2] mac80211: A-MPDU Tx add session's and low level driver's API

This patch adds the API for 3 stages in A-MPDU Tx session flow:
- request mac80211 to start/stop A-MPDU Tx session for specific TID. such a
request should be issued by a load aware element, either mac80211 itself
or external element.
- requests by mac80211 to low-level driver to start/stop Tx aggregation.
notice that low level driver responds now with Starting Sequence Number.
- async feedback by low-level to mac80211 to inform that HW is ready for
next A-MPDU Tx state.
Changes in API to Rx A-MPDU were also made, reflected in iwlwifi changes as
well.

Signed-off-by: Ron Rindjunsky <[email protected]>
---
drivers/net/wireless/iwlwifi/iwl-4965.c | 4 +-
drivers/net/wireless/iwlwifi/iwl-4965.h | 2 +-
include/net/mac80211.h | 85 ++++++++++++++++++++++++++++++-
net/mac80211/ieee80211_sta.c | 4 +-
4 files changed, 88 insertions(+), 7 deletions(-)

Index: wl2_6_24_rc8_ev/drivers/net/wireless/iwlwifi/iwl-4965.c
===================================================================
--- wl2_6_24_rc8_ev.orig/drivers/net/wireless/iwlwifi/iwl-4965.c
+++ wl2_6_24_rc8_ev/drivers/net/wireless/iwlwifi/iwl-4965.c
@@ -4719,7 +4719,7 @@ static void iwl4965_sta_modify_del_ba_ti

int iwl4965_mac_ampdu_action(struct ieee80211_hw *hw,
enum ieee80211_ampdu_mlme_action action,
- const u8 *addr, u16 tid, u16 ssn)
+ const u8 *addr, u16 tid, u16 *ssn)
{
struct iwl4965_priv *priv = hw->priv;
int sta_id;
@@ -4731,7 +4731,7 @@ int iwl4965_mac_ampdu_action(struct ieee
switch (action) {
case IEEE80211_AMPDU_RX_START:
IWL_DEBUG_HT("start Rx\n");
- iwl4965_sta_modify_add_ba_tid(priv, sta_id, tid, ssn);
+ iwl4965_sta_modify_add_ba_tid(priv, sta_id, tid, *ssn);
break;
case IEEE80211_AMPDU_RX_STOP:
IWL_DEBUG_HT("stop Rx\n");
Index: wl2_6_24_rc8_ev/drivers/net/wireless/iwlwifi/iwl-4965.h
===================================================================
--- wl2_6_24_rc8_ev.orig/drivers/net/wireless/iwlwifi/iwl-4965.h
+++ wl2_6_24_rc8_ev/drivers/net/wireless/iwlwifi/iwl-4965.h
@@ -777,7 +777,7 @@ extern void iwl4965_set_ht_add_station(s
struct ieee80211_ht_info *sta_ht_inf);
extern int iwl4965_mac_ampdu_action(struct ieee80211_hw *hw,
enum ieee80211_ampdu_mlme_action action,
- const u8 *addr, u16 tid, u16 ssn);
+ const u8 *addr, u16 tid, u16 *ssn);
#ifdef CONFIG_IWL4965_HT_AGG
extern int iwl4965_mac_ht_tx_agg_start(struct ieee80211_hw *hw, u8 *da,
u16 tid, u16 *start_seq_num);
Index: wl2_6_24_rc8_ev/include/net/mac80211.h
===================================================================
--- wl2_6_24_rc8_ev.orig/include/net/mac80211.h
+++ wl2_6_24_rc8_ev/include/net/mac80211.h
@@ -967,10 +967,14 @@ enum ieee80211_filter_flags {
* &struct ieee80211_ops to indicate which action is needed.
* @IEEE80211_AMPDU_RX_START: start Rx aggregation
* @IEEE80211_AMPDU_RX_STOP: stop Rx aggregation
+ * @IEEE80211_AMPDU_TX_START: start Tx aggregation
+ * @IEEE80211_AMPDU_TX_STOP: stop Tx aggregation
*/
enum ieee80211_ampdu_mlme_action {
IEEE80211_AMPDU_RX_START,
IEEE80211_AMPDU_RX_STOP,
+ IEEE80211_AMPDU_TX_START,
+ IEEE80211_AMPDU_TX_STOP,
};

/**
@@ -1111,7 +1115,8 @@ enum ieee80211_ampdu_mlme_action {
* The RA/TID combination determines the destination and TID we want
* the ampdu action to be performed for. The action is defined through
* ieee80211_ampdu_mlme_action. Starting sequence number (@ssn)
- * is the first frame we expect to perform the action on.
+ * is the first frame we expect to perform the action on. notice
+ * that TX/RX_STOP can pass NULL for this parameter.
*/
struct ieee80211_ops {
int (*tx)(struct ieee80211_hw *hw, struct sk_buff *skb,
@@ -1162,7 +1167,7 @@ struct ieee80211_ops {
int (*conf_ht)(struct ieee80211_hw *hw, struct ieee80211_conf *conf);
int (*ampdu_action)(struct ieee80211_hw *hw,
enum ieee80211_ampdu_mlme_action action,
- const u8 *ra, u16 tid, u16 ssn);
+ const u8 *addr, u16 tid, u16 *ssn);
};

/**
@@ -1574,4 +1579,81 @@ void ieee80211_iterate_active_interfaces
struct ieee80211_vif *vif),
void *data);

+/**
+ * ieee80211_start_tx_ba_session - Start a tx Block Ack session.
+ * @hw: pointer as obtained from ieee80211_alloc_hw().
+ * @ra: receiver address of the BA session recipient
+ * @tid: the TID to BA on.
+ * @return: success if addBA request was sent, failure otherwise
+ *
+ * Although mac80211/low level driver/user space application can estimate
+ * the need to start aggregation on a certain RA/TID, the session level
+ * will be managed by the mac80211.
+ */
+int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid);
+
+/**
+ * ieee80211_start_tx_ba_cb - low level driver ready to aggregate.
+ * @hw: pointer as obtained from ieee80211_alloc_hw().
+ * @ra: receiver address of the BA session recipient.
+ * @tid: the TID to BA on.
+ *
+ * This function must be called by low level driver once it has
+ * finished with preparations for the BA session.
+ */
+void ieee80211_start_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u16 tid);
+
+/**
+ * ieee80211_start_tx_ba_cb_irqsafe - low level driver ready to aggregate.
+ * @hw: pointer as obtained from ieee80211_alloc_hw().
+ * @ra: receiver address of the BA session recipient.
+ * @tid: the TID to BA on.
+ *
+ * This function must be called by low level driver once it has
+ * finished with preparations for the BA session.
+ * This version of the function is irq safe.
+ */
+void ieee80211_start_tx_ba_cb_irqsafe(struct ieee80211_hw *hw, const u8 *ra,
+ u16 tid);
+
+/**
+ * ieee80211_stop_tx_ba_session - Stop a Block Ack session.
+ * @hw: pointer as obtained from ieee80211_alloc_hw().
+ * @ra: receiver address of the BA session recipient
+ * @tid: the TID to stop BA.
+ * @initiator: if indicates initiator DELBA frame will be sent.
+ * @return: error if no sta with matching da found, success otherwise
+ *
+ * Although mac80211/low level driver/user space application can estimate
+ * the need to stop aggregation on a certain RA/TID, the session level
+ * will be managed by the mac80211.
+ */
+int ieee80211_stop_tx_ba_session(struct ieee80211_hw *hw,
+ u8 *ra, u16 tid,
+ enum ieee80211_back_parties initiator);
+
+/**
+ * ieee80211_stop_tx_ba_cb - low level driver ready to stop aggregate.
+ * @hw: pointer as obtained from ieee80211_alloc_hw().
+ * @ra: receiver address of the BA session recipient.
+ * @tid: the desired TID to BA on.
+ *
+ * This function must be called by low level driver once it has
+ * finished with preparations for the BA session tear down.
+ */
+void ieee80211_stop_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u8 tid);
+
+/**
+ * ieee80211_stop_tx_ba_cb_irqsafe - low level driver ready to stop aggregate.
+ * @hw: pointer as obtained from ieee80211_alloc_hw().
+ * @ra: receiver address of the BA session recipient.
+ * @tid: the desired TID to BA on.
+ *
+ * This function must be called by low level driver once it has
+ * finished with preparations for the BA session tear down.
+ * This version of the function is irq safe.
+ */
+void ieee80211_stop_tx_ba_cb_irqsafe(struct ieee80211_hw *hw, const u8 *ra,
+ u16 tid);
+
#endif /* MAC80211_H */
Index: wl2_6_24_rc8_ev/net/mac80211/ieee80211_sta.c
===================================================================
--- wl2_6_24_rc8_ev.orig/net/mac80211/ieee80211_sta.c
+++ wl2_6_24_rc8_ev/net/mac80211/ieee80211_sta.c
@@ -1126,7 +1126,7 @@ static void ieee80211_sta_process_addba_

if (local->ops->ampdu_action)
ret = local->ops->ampdu_action(hw, IEEE80211_AMPDU_RX_START,
- sta->addr, tid, start_seq_num);
+ sta->addr, tid, &start_seq_num);
#ifdef CONFIG_MAC80211_HT_DEBUG
printk(KERN_DEBUG "Rx A-MPDU on tid %d result %d", tid, ret);
#endif /* CONFIG_MAC80211_HT_DEBUG */
@@ -1228,7 +1228,7 @@ void ieee80211_sta_stop_rx_ba_session(st
BUG_ON(!local->ops->ampdu_action);

ret = local->ops->ampdu_action(hw, IEEE80211_AMPDU_RX_STOP,
- ra, tid, EINVAL);
+ ra, tid, NULL);
if (ret)
printk(KERN_DEBUG "HW problem - can not stop rx "
"aggergation for tid %d\n", tid);

2008-01-28 12:07:40

by Ron Rindjunsky

[permalink] [raw]
Subject: [PATCH 05/12 v2] mac80211: A-MPDU Tx MLME data initialization

This patch initialize A-MPDU MLME data for Tx sessions.

Signed-off-by: Ron Rindjunsky <[email protected]>
---
net/mac80211/sta_info.c | 13 ++++++++++++-
1 files changed, 12 insertions(+), 1 deletions(-)

diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index c964082..7c65849 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -117,8 +117,10 @@ static void sta_info_release(struct kref *kref)
while ((skb = skb_dequeue(&sta->tx_filtered)) != NULL) {
dev_kfree_skb_any(skb);
}
- for (i = 0; i < STA_TID_NUM; i++)
+ for (i = 0; i < STA_TID_NUM; i++) {
del_timer_sync(&sta->ampdu_mlme.tid_rx[i].session_timer);
+ del_timer_sync(&sta->ampdu_mlme.tid_tx[i].addba_resp_timer);
+ }
rate_control_free_sta(sta->rate_ctrl, sta->rate_ctrl_priv);
rate_control_put(sta->rate_ctrl);
kfree(sta);
@@ -157,17 +159,26 @@ struct sta_info * sta_info_add(struct ieee80211_local *local,
sta->local = local;
sta->dev = dev;
spin_lock_init(&sta->ampdu_mlme.ampdu_rx);
+ spin_lock_init(&sta->ampdu_mlme.ampdu_tx);
for (i = 0; i < STA_TID_NUM; i++) {
/* timer_to_tid must be initialized with identity mapping to
* enable session_timer's data differentiation. refer to
* sta_rx_agg_session_timer_expired for useage */
sta->timer_to_tid[i] = i;
+ /* tid to tx queue: initialize according to HW (0 is valid) */
+ sta->tid_to_tx_q[i] = local->hw.queues;
/* rx timers */
sta->ampdu_mlme.tid_rx[i].session_timer.function =
sta_rx_agg_session_timer_expired;
sta->ampdu_mlme.tid_rx[i].session_timer.data =
(unsigned long)&sta->timer_to_tid[i];
init_timer(&sta->ampdu_mlme.tid_rx[i].session_timer);
+ /* tx timers */
+ sta->ampdu_mlme.tid_tx[i].addba_resp_timer.function =
+ sta_addba_resp_timer_expired;
+ sta->ampdu_mlme.tid_tx[i].addba_resp_timer.data =
+ (unsigned long)&sta->timer_to_tid[i];
+ init_timer(&sta->ampdu_mlme.tid_tx[i].addba_resp_timer);
}
skb_queue_head_init(&sta->ps_tx_buf);
skb_queue_head_init(&sta->tx_filtered);
--
1.5.3.3


2008-01-28 12:07:40

by Ron Rindjunsky

[permalink] [raw]
Subject: [PATCH 07/12 v2] mac80211: A-MPDU Tx change tx_status to support Block Ack data

This patch adds fields to ieee80211_tx_status in order to allow block ack
information exchange between low-level driver,mac80211 and rate scaling
module.

Signed-off-by: Ron Rindjunsky <[email protected]>
---
include/net/mac80211.h | 21 ++++++++++++---------
1 files changed, 12 insertions(+), 9 deletions(-)

Index: wl2_6_24_rc8_ev/include/net/mac80211.h
===================================================================
--- wl2_6_24_rc8_ev.orig/include/net/mac80211.h
+++ wl2_6_24_rc8_ev/include/net/mac80211.h
@@ -445,12 +445,14 @@ struct ieee80211_rx_status {
*
* @IEEE80211_TX_STATUS_TX_FILTERED: The frame was not transmitted
* because the destination STA was in powersave mode.
- *
* @IEEE80211_TX_STATUS_ACK: Frame was acknowledged
+ * @IEEE80211_TX_STATUS_AMPDU: The frame was aggregated, so status
+ * is for the whole aggregation.
*/
enum ieee80211_tx_status_flags {
IEEE80211_TX_STATUS_TX_FILTERED = 1<<0,
IEEE80211_TX_STATUS_ACK = 1<<1,
+ IEEE80211_TX_STATUS_AMPDU = 1<<2,
};

/**
@@ -461,24 +463,25 @@ enum ieee80211_tx_status_flags {
*
* @control: a copy of the &struct ieee80211_tx_control passed to the driver
* in the tx() callback.
- *
* @flags: transmit status flags, defined above
- *
- * @ack_signal: signal strength of the ACK frame
- *
+ * @retry_count: number of retries
* @excessive_retries: set to 1 if the frame was retried many times
* but not acknowledged
- *
- * @retry_count: number of retries
- *
+ * @ampdu_ack_len: number of aggregated frames.
+ * relevant only if IEEE80211_TX_STATUS_AMPDU was set.
+ * @ampdu_ack_map: block ack bit map for the aggregation.
+ * relevant only if IEEE80211_TX_STATUS_AMPDU was set.
+ * @ack_signal: signal strength of the ACK frame
* @queue_length: ?? REMOVE
* @queue_number: ?? REMOVE
*/
struct ieee80211_tx_status {
struct ieee80211_tx_control control;
u8 flags;
- bool excessive_retries;
u8 retry_count;
+ bool excessive_retries;
+ u8 ampdu_ack_len;
+ u64 ampdu_ack_map;
int ack_signal;
int queue_length;
int queue_number;

2008-01-28 12:07:38

by Ron Rindjunsky

[permalink] [raw]
Subject: [PATCH 08/12 v2] mac80211: A-MPDU Tx add delBA from recipient support

This patch adds the ability to handle delBA from recipient to initiator
during an A-MPDU session

Signed-off-by: Ron Rindjunsky <[email protected]>
---
net/mac80211/ieee80211_sta.c | 13 +++++++++++--
1 files changed, 11 insertions(+), 2 deletions(-)

Index: wl2_6_24_rc8_ev/net/mac80211/ieee80211_sta.c
===================================================================
--- wl2_6_24_rc8_ev.orig/net/mac80211/ieee80211_sta.c
+++ wl2_6_24_rc8_ev/net/mac80211/ieee80211_sta.c
@@ -1401,14 +1401,23 @@ static void ieee80211_sta_process_delba(

#ifdef CONFIG_MAC80211_HT_DEBUG
if (net_ratelimit())
- printk(KERN_DEBUG "delba from %s on tid %d reason code %d\n",
- print_mac(mac, mgmt->sa), tid,
+ printk(KERN_DEBUG "delba from %s (%s) tid %d reason code %d\n",
+ print_mac(mac, mgmt->sa),
+ initiator ? "recipient" : "initiator", tid,
mgmt->u.action.u.delba.reason_code);
#endif /* CONFIG_MAC80211_HT_DEBUG */

if (initiator == WLAN_BACK_INITIATOR)
ieee80211_sta_stop_rx_ba_session(dev, sta->addr, tid,
WLAN_BACK_INITIATOR, 0);
+ else { /* WLAN_BACK_RECIPIENT */
+ spin_lock_bh(&sta->ampdu_mlme.ampdu_tx);
+ sta->ampdu_mlme.tid_tx[tid].state =
+ HT_AGG_STATE_OPERATIONAL;
+ spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx);
+ ieee80211_stop_tx_ba_session(&local->hw, sta->addr, tid,
+ WLAN_BACK_RECIPIENT);
+ }
sta_info_put(sta);
}


2008-01-28 12:07:41

by Ron Rindjunsky

[permalink] [raw]
Subject: [PATCH 09/12 v2] iwlwifi: A-MPDU Tx conform API to mac80211

This patch alters the current API in order to fit the new
API mac80211 gives for A-MPDU Tx

Signed-off-by: Ron Rindjunsky <[email protected]>
---
drivers/net/wireless/iwlwifi/iwl-4965.c | 77 ++++++++++++++++----------
drivers/net/wireless/iwlwifi/iwl-4965.h | 4 --
drivers/net/wireless/iwlwifi/iwl4965-base.c | 4 --
3 files changed, 47 insertions(+), 38 deletions(-)

Index: wl2_6_24_rc8_ev/drivers/net/wireless/iwlwifi/iwl-4965.c
===================================================================
--- wl2_6_24_rc8_ev.orig/drivers/net/wireless/iwlwifi/iwl-4965.c
+++ wl2_6_24_rc8_ev/drivers/net/wireless/iwlwifi/iwl-4965.c
@@ -4717,34 +4717,6 @@ static void iwl4965_sta_modify_del_ba_ti
iwl4965_send_add_station(priv, &priv->stations[sta_id].sta, CMD_ASYNC);
}

-int iwl4965_mac_ampdu_action(struct ieee80211_hw *hw,
- enum ieee80211_ampdu_mlme_action action,
- const u8 *addr, u16 tid, u16 *ssn)
-{
- struct iwl4965_priv *priv = hw->priv;
- int sta_id;
- DECLARE_MAC_BUF(mac);
-
- IWL_DEBUG_HT("A-MPDU action on da=%s tid=%d ",
- print_mac(mac, addr), tid);
- sta_id = iwl4965_hw_find_station(priv, addr);
- switch (action) {
- case IEEE80211_AMPDU_RX_START:
- IWL_DEBUG_HT("start Rx\n");
- iwl4965_sta_modify_add_ba_tid(priv, sta_id, tid, *ssn);
- break;
- case IEEE80211_AMPDU_RX_STOP:
- IWL_DEBUG_HT("stop Rx\n");
- iwl4965_sta_modify_del_ba_tid(priv, sta_id, tid);
- break;
- default:
- IWL_DEBUG_HT("unknown\n");
- return -EINVAL;
- break;
- }
- return 0;
-}
-
#ifdef CONFIG_IWL4965_HT_AGG

static const u16 default_tid_to_tx_fifo[] = {
@@ -4835,8 +4807,7 @@ int iwl4965_mac_ht_tx_agg_start(struct i
}


-int iwl4965_mac_ht_tx_agg_stop(struct ieee80211_hw *hw, u8 *da, u16 tid,
- int generator)
+int iwl4965_mac_ht_tx_agg_stop(struct ieee80211_hw *hw, u8 *da, u16 tid)
{

struct iwl4965_priv *priv = hw->priv;
@@ -4878,6 +4849,52 @@ int iwl4965_mac_ht_tx_agg_stop(struct ie


#endif /* CONFIG_IWL4965_HT_AGG */
+
+int iwl4965_mac_ht_tx_agg_start(struct ieee80211_hw *hw, const u8 *da, u16 tid,
+ u16 *start_seq_num)
+{
+ return 0;
+}
+
+int iwl4965_mac_ht_tx_agg_stop(struct ieee80211_hw *hw, const u8 *da, u16 tid)
+{
+ return 0;
+}
+
+int iwl4965_mac_ampdu_action(struct ieee80211_hw *hw,
+ enum ieee80211_ampdu_mlme_action action,
+ const u8 *addr, u16 tid, u16 *ssn)
+{
+ struct iwl4965_priv *priv = hw->priv;
+ int sta_id;
+ DECLARE_MAC_BUF(mac);
+
+ IWL_DEBUG_HT("A-MPDU action on da=%s tid=%d ",
+ print_mac(mac, addr), tid);
+ sta_id = iwl4965_hw_find_station(priv, addr);
+ switch (action) {
+ case IEEE80211_AMPDU_RX_START:
+ IWL_DEBUG_HT("start Rx\n");
+ iwl4965_sta_modify_add_ba_tid(priv, sta_id, tid, *ssn);
+ break;
+ case IEEE80211_AMPDU_RX_STOP:
+ IWL_DEBUG_HT("stop Rx\n");
+ iwl4965_sta_modify_del_ba_tid(priv, sta_id, tid);
+ break;
+ case IEEE80211_AMPDU_TX_START:
+ IWL_DEBUG_HT("start Tx\n");
+ return iwl4965_mac_ht_tx_agg_start(hw, addr, tid, ssn);
+ case IEEE80211_AMPDU_TX_STOP:
+ IWL_DEBUG_HT("stop Tx\n");
+ return iwl4965_mac_ht_tx_agg_stop(hw, addr, tid);
+ default:
+ IWL_DEBUG_HT("unknown\n");
+ return -EINVAL;
+ break;
+ }
+ return 0;
+}
+
#endif /* CONFIG_IWL4965_HT */

/* Set up 4965-specific Rx frame reply handlers */
Index: wl2_6_24_rc8_ev/drivers/net/wireless/iwlwifi/iwl-4965.h
===================================================================
--- wl2_6_24_rc8_ev.orig/drivers/net/wireless/iwlwifi/iwl-4965.h
+++ wl2_6_24_rc8_ev/drivers/net/wireless/iwlwifi/iwl-4965.h
@@ -779,10 +779,6 @@ extern int iwl4965_mac_ampdu_action(stru
enum ieee80211_ampdu_mlme_action action,
const u8 *addr, u16 tid, u16 *ssn);
#ifdef CONFIG_IWL4965_HT_AGG
-extern int iwl4965_mac_ht_tx_agg_start(struct ieee80211_hw *hw, u8 *da,
- u16 tid, u16 *start_seq_num);
-extern int iwl4965_mac_ht_tx_agg_stop(struct ieee80211_hw *hw, u8 *da,
- u16 tid, int generator);
extern void iwl4965_turn_off_agg(struct iwl4965_priv *priv, u8 tid);
extern void iwl4965_tl_get_stats(struct iwl4965_priv *priv,
struct ieee80211_hdr *hdr);
Index: wl2_6_24_rc8_ev/drivers/net/wireless/iwlwifi/iwl4965-base.c
===================================================================
--- wl2_6_24_rc8_ev.orig/drivers/net/wireless/iwlwifi/iwl4965-base.c
+++ wl2_6_24_rc8_ev/drivers/net/wireless/iwlwifi/iwl4965-base.c
@@ -9012,10 +9012,6 @@ static struct ieee80211_ops iwl4965_hw_o
#ifdef CONFIG_IWL4965_HT
.conf_ht = iwl4965_mac_conf_ht,
.ampdu_action = iwl4965_mac_ampdu_action,
-#ifdef CONFIG_IWL4965_HT_AGG
- .ht_tx_agg_start = iwl4965_mac_ht_tx_agg_start,
- .ht_tx_agg_stop = iwl4965_mac_ht_tx_agg_stop,
-#endif /* CONFIG_IWL4965_HT_AGG */
#endif /* CONFIG_IWL4965_HT */
.hw_scan = iwl4965_mac_hw_scan
};