Commit fe01b477 authored by Ron Rindjunsky's avatar Ron Rindjunsky Committed by John W. Linville

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: default avatarRon Rindjunsky <ron.rindjunsky@intel.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 8114fcf1
...@@ -1300,6 +1300,25 @@ struct iwl4965_tx_resp { ...@@ -1300,6 +1300,25 @@ struct iwl4965_tx_resp {
__le32 status; /* TX status (for aggregation status of 1st frame) */ __le32 status; /* TX status (for aggregation status of 1st frame) */
} __attribute__ ((packed)); } __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) * REPLY_COMPRESSED_BA = 0xc5 (response only, not a command)
* *
...@@ -1313,9 +1332,8 @@ struct iwl4965_compressed_ba_resp { ...@@ -1313,9 +1332,8 @@ struct iwl4965_compressed_ba_resp {
/* Index of recipient (BA-sending) station in uCode's station table */ /* Index of recipient (BA-sending) station in uCode's station table */
u8 sta_id; u8 sta_id;
u8 tid; u8 tid;
__le16 ba_seq_ctl; __le16 seq_ctl;
__le32 ba_bitmap0; __le64 bitmap;
__le32 ba_bitmap1;
__le16 scd_flow; __le16 scd_flow;
__le16 scd_ssn; __le16 scd_ssn;
} __attribute__ ((packed)); } __attribute__ ((packed));
......
This diff is collapsed.
...@@ -433,7 +433,6 @@ struct iwl4965_rx_queue { ...@@ -433,7 +433,6 @@ struct iwl4965_rx_queue {
#define IWL_INVALID_VALUE -1 #define IWL_INVALID_VALUE -1
#ifdef CONFIG_IWL4965_HT #ifdef CONFIG_IWL4965_HT
#ifdef CONFIG_IWL4965_HT_AGG
/** /**
* struct iwl4965_ht_agg -- aggregation status while waiting for block-ack * struct iwl4965_ht_agg -- aggregation status while waiting for block-ack
* @txq_id: Tx queue used for Tx attempt * @txq_id: Tx queue used for Tx attempt
...@@ -453,19 +452,22 @@ struct iwl4965_ht_agg { ...@@ -453,19 +452,22 @@ struct iwl4965_ht_agg {
u16 frame_count; u16 frame_count;
u16 wait_for_ba; u16 wait_for_ba;
u16 start_idx; u16 start_idx;
u32 bitmap0; u64 bitmap;
u32 bitmap1;
u32 rate_n_flags; 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 */ #endif /* CONFIG_IWL4965_HT */
struct iwl4965_tid_data { struct iwl4965_tid_data {
u16 seq_number; u16 seq_number;
u16 tfds_in_queue;
#ifdef CONFIG_IWL4965_HT #ifdef CONFIG_IWL4965_HT
#ifdef CONFIG_IWL4965_HT_AGG
struct iwl4965_ht_agg agg; struct iwl4965_ht_agg agg;
#endif /* CONFIG_IWL4965_HT_AGG */
#endif /* CONFIG_IWL4965_HT */ #endif /* CONFIG_IWL4965_HT */
}; };
...@@ -743,7 +745,7 @@ extern u8 iwl4965_hw_find_station(struct iwl4965_priv *priv, const u8 *bssid); ...@@ -743,7 +745,7 @@ extern u8 iwl4965_hw_find_station(struct iwl4965_priv *priv, const u8 *bssid);
extern int iwl4965_hw_channel_switch(struct iwl4965_priv *priv, u16 channel); 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_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; struct iwl4965_priv;
/* /*
...@@ -778,6 +780,8 @@ extern void iwl4965_set_ht_add_station(struct iwl4965_priv *priv, u8 index, ...@@ -778,6 +780,8 @@ extern void iwl4965_set_ht_add_station(struct iwl4965_priv *priv, u8 index,
extern int iwl4965_mac_ampdu_action(struct ieee80211_hw *hw, extern int iwl4965_mac_ampdu_action(struct ieee80211_hw *hw,
enum ieee80211_ampdu_mlme_action action, enum ieee80211_ampdu_mlme_action action,
const u8 *addr, u16 tid, u16 *ssn); 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 #ifdef CONFIG_IWL4965_HT_AGG
extern void iwl4965_turn_off_agg(struct iwl4965_priv *priv, u8 tid); extern void iwl4965_turn_off_agg(struct iwl4965_priv *priv, u8 tid);
extern void iwl4965_tl_get_stats(struct iwl4965_priv *priv, extern void iwl4965_tl_get_stats(struct iwl4965_priv *priv,
......
...@@ -205,7 +205,7 @@ static void iwl4965_print_hex_dump(int level, void *p, u32 len) ...@@ -205,7 +205,7 @@ static void iwl4965_print_hex_dump(int level, void *p, u32 len)
* See more detailed info in iwl-4965-hw.h. * 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; int s = q->read_ptr - q->write_ptr;
...@@ -2972,11 +2972,10 @@ static int iwl4965_tx_skb(struct iwl4965_priv *priv, ...@@ -2972,11 +2972,10 @@ static int iwl4965_tx_skb(struct iwl4965_priv *priv,
__constant_cpu_to_le16(IEEE80211_SCTL_FRAG)); __constant_cpu_to_le16(IEEE80211_SCTL_FRAG));
seq_number += 0x10; seq_number += 0x10;
#ifdef CONFIG_IWL4965_HT #ifdef CONFIG_IWL4965_HT
#ifdef CONFIG_IWL4965_HT_AGG
/* aggregation is on for this <sta,tid> */ /* 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; 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 */ #endif /* CONFIG_IWL4965_HT */
} }
...@@ -3528,10 +3527,10 @@ int iwl4965_tx_queue_reclaim(struct iwl4965_priv *priv, int txq_id, int index) ...@@ -3528,10 +3527,10 @@ int iwl4965_tx_queue_reclaim(struct iwl4965_priv *priv, int txq_id, int index)
nfreed++; 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) && (txq_id != IWL_CMD_QUEUE_NUM) &&
priv->mac80211_registered) priv->mac80211_registered)
ieee80211_wake_queue(priv->hw, txq_id); ieee80211_wake_queue(priv->hw, txq_id); */
return nfreed; return nfreed;
...@@ -3550,7 +3549,6 @@ static int iwl4965_is_tx_success(u32 status) ...@@ -3550,7 +3549,6 @@ static int iwl4965_is_tx_success(u32 status)
* *
******************************************************************************/ ******************************************************************************/
#ifdef CONFIG_IWL4965_HT #ifdef CONFIG_IWL4965_HT
#ifdef CONFIG_IWL4965_HT_AGG
static inline int iwl4965_get_ra_sta_id(struct iwl4965_priv *priv, static inline int iwl4965_get_ra_sta_id(struct iwl4965_priv *priv,
struct ieee80211_hdr *hdr) struct ieee80211_hdr *hdr)
...@@ -3585,11 +3583,11 @@ static inline u32 iwl4965_get_scd_ssn(struct iwl4965_tx_resp *tx_resp) ...@@ -3585,11 +3583,11 @@ static inline u32 iwl4965_get_scd_ssn(struct iwl4965_tx_resp *tx_resp)
*/ */
static int iwl4965_tx_status_reply_tx(struct iwl4965_priv *priv, static int iwl4965_tx_status_reply_tx(struct iwl4965_priv *priv,
struct iwl4965_ht_agg *agg, struct iwl4965_ht_agg *agg,
struct iwl4965_tx_resp *tx_resp, struct iwl4965_tx_resp_agg *tx_resp,
u16 start_idx) u16 start_idx)
{ {
u32 status; u16 status;
__le32 *frame_status = &tx_resp->status; struct agg_tx_status *frame_status = &tx_resp->status;
struct ieee80211_tx_status *tx_status = NULL; struct ieee80211_tx_status *tx_status = NULL;
struct ieee80211_hdr *hdr = NULL; struct ieee80211_hdr *hdr = NULL;
int i, sh; int i, sh;
...@@ -3602,26 +3600,25 @@ static int iwl4965_tx_status_reply_tx(struct iwl4965_priv *priv, ...@@ -3602,26 +3600,25 @@ static int iwl4965_tx_status_reply_tx(struct iwl4965_priv *priv,
agg->frame_count = tx_resp->frame_count; agg->frame_count = tx_resp->frame_count;
agg->start_idx = start_idx; agg->start_idx = start_idx;
agg->rate_n_flags = le32_to_cpu(tx_resp->rate_n_flags); 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 */ /* # frames attempted by Tx command */
if (agg->frame_count == 1) { if (agg->frame_count == 1) {
/* Only one frame was attempted; no block-ack will arrive */ /* Only one frame was attempted; no block-ack will arrive */
struct iwl4965_tx_queue *txq ; status = le16_to_cpu(frame_status[0].status);
status = le32_to_cpu(frame_status[0]); 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 */ /* FIXME: code repetition */
IWL_DEBUG_TX_REPLY("FrameCnt = %d, StartIdx=%d \n", IWL_DEBUG_TX_REPLY("FrameCnt = %d, StartIdx=%d idx=%d\n",
agg->frame_count, agg->start_idx); 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->retry_count = tx_resp->failure_frame;
tx_status->queue_number = status & 0xff; 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)? tx_status->flags = iwl4965_is_tx_success(status)?
IEEE80211_TX_STATUS_ACK : 0; IEEE80211_TX_STATUS_ACK : 0;
tx_status->control.tx_rate = tx_status->control.tx_rate =
...@@ -3642,8 +3639,8 @@ static int iwl4965_tx_status_reply_tx(struct iwl4965_priv *priv, ...@@ -3642,8 +3639,8 @@ static int iwl4965_tx_status_reply_tx(struct iwl4965_priv *priv,
/* Construct bit-map of pending frames within Tx window */ /* Construct bit-map of pending frames within Tx window */
for (i = 0; i < agg->frame_count; i++) { for (i = 0; i < agg->frame_count; i++) {
u16 sc; u16 sc;
status = le32_to_cpu(frame_status[i]); status = le16_to_cpu(frame_status[i].status);
seq = status >> 16; seq = le16_to_cpu(frame_status[i].sequence);
idx = SEQ_TO_INDEX(seq); idx = SEQ_TO_INDEX(seq);
txq_id = SEQ_TO_QUEUE(seq); txq_id = SEQ_TO_QUEUE(seq);
...@@ -3687,13 +3684,12 @@ static int iwl4965_tx_status_reply_tx(struct iwl4965_priv *priv, ...@@ -3687,13 +3684,12 @@ static int iwl4965_tx_status_reply_tx(struct iwl4965_priv *priv,
start, (u32)(bitmap & 0xFFFFFFFF)); start, (u32)(bitmap & 0xFFFFFFFF));
} }
agg->bitmap0 = bitmap & 0xFFFFFFFF; agg->bitmap = bitmap;
agg->bitmap1 = bitmap >> 32;
agg->start_idx = start; agg->start_idx = start;
agg->rate_n_flags = le32_to_cpu(tx_resp->rate_n_flags); 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->frame_count, agg->start_idx,
agg->bitmap0); agg->bitmap);
if (bitmap) if (bitmap)
agg->wait_for_ba = 1; agg->wait_for_ba = 1;
...@@ -3701,7 +3697,6 @@ static int iwl4965_tx_status_reply_tx(struct iwl4965_priv *priv, ...@@ -3701,7 +3697,6 @@ static int iwl4965_tx_status_reply_tx(struct iwl4965_priv *priv,
return 0; return 0;
} }
#endif #endif
#endif
/** /**
* iwl4965_rx_reply_tx - Handle standard (non-aggregation) Tx response * iwl4965_rx_reply_tx - Handle standard (non-aggregation) Tx response
...@@ -3718,9 +3713,9 @@ static void iwl4965_rx_reply_tx(struct iwl4965_priv *priv, ...@@ -3718,9 +3713,9 @@ static void iwl4965_rx_reply_tx(struct iwl4965_priv *priv,
struct iwl4965_tx_resp *tx_resp = (void *)&pkt->u.raw[0]; struct iwl4965_tx_resp *tx_resp = (void *)&pkt->u.raw[0];
u32 status = le32_to_cpu(tx_resp->status); u32 status = le32_to_cpu(tx_resp->status);
#ifdef CONFIG_IWL4965_HT #ifdef CONFIG_IWL4965_HT
#ifdef CONFIG_IWL4965_HT_AGG int tid = MAX_TID_COUNT, sta_id = IWL_INVALID_STATION;
int tid, sta_id; struct ieee80211_hdr *hdr;
#endif __le16 *qc;
#endif #endif
if ((index >= txq->q.n_bd) || (x2_queue_used(&txq->q, index) == 0)) { if ((index >= txq->q.n_bd) || (x2_queue_used(&txq->q, index) == 0)) {
...@@ -3732,44 +3727,51 @@ static void iwl4965_rx_reply_tx(struct iwl4965_priv *priv, ...@@ -3732,44 +3727,51 @@ static void iwl4965_rx_reply_tx(struct iwl4965_priv *priv,
} }
#ifdef CONFIG_IWL4965_HT #ifdef CONFIG_IWL4965_HT
#ifdef CONFIG_IWL4965_HT_AGG hdr = iwl4965_tx_queue_get_hdr(priv, txq_id, index);
if (txq->sched_retry) { qc = ieee80211_get_qos_ctrl(hdr);
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");
return;
}
if (qc)
tid = le16_to_cpu(*qc) & 0xf; tid = le16_to_cpu(*qc) & 0xf;
sta_id = iwl4965_get_ra_sta_id(priv, hdr); sta_id = iwl4965_get_ra_sta_id(priv, hdr);
if (unlikely(sta_id == IWL_INVALID_STATION)) { if (txq->sched_retry && unlikely(sta_id == IWL_INVALID_STATION)) {
IWL_ERROR("Station not known for\n"); IWL_ERROR("Station not known\n");
return; return;
} }
if (txq->sched_retry) {
const u32 scd_ssn = iwl4965_get_scd_ssn(tx_resp);
struct iwl4965_ht_agg *agg = NULL;
if (!qc)
return;
agg = &priv->stations[sta_id].tid[tid].agg; 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) && if ((tx_resp->frame_count == 1) &&
!iwl4965_is_tx_success(status)) { !iwl4965_is_tx_success(status)) {
/* TODO: send BAR */ /* 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); index = iwl4965_queue_dec_wrap(scd_ssn & 0xff, txq->q.n_bd);
IWL_DEBUG_TX_REPLY("Retry scheduler reclaim scd_ssn " IWL_DEBUG_TX_REPLY("Retry scheduler reclaim scd_ssn "
"%d index %d\n", scd_ssn , index); "%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 { } else {
#endif /* CONFIG_IWL4965_HT_AGG */
#endif /* CONFIG_IWL4965_HT */ #endif /* CONFIG_IWL4965_HT */
tx_status = &(txq->txb[txq->q.read_ptr].status); tx_status = &(txq->txb[txq->q.read_ptr].status);
...@@ -3790,12 +3792,21 @@ static void iwl4965_rx_reply_tx(struct iwl4965_priv *priv, ...@@ -3790,12 +3792,21 @@ static void iwl4965_rx_reply_tx(struct iwl4965_priv *priv,
tx_resp->failure_frame); tx_resp->failure_frame);
IWL_DEBUG_TX_REPLY("Tx queue reclaim %d\n", index); IWL_DEBUG_TX_REPLY("Tx queue reclaim %d\n", index);
if (index != -1) if (index != -1) {
iwl4965_tx_queue_reclaim(priv, txq_id, index); 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
#ifdef CONFIG_IWL4965_HT_AGG
} }
#endif /* CONFIG_IWL4965_HT_AGG */
#endif /* CONFIG_IWL4965_HT */ #endif /* CONFIG_IWL4965_HT */
if (iwl_check_bits(status, TX_ABORT_REQUIRED_MSK)) if (iwl_check_bits(status, TX_ABORT_REQUIRED_MSK))
...@@ -9089,10 +9100,8 @@ static int iwl4965_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e ...@@ -9089,10 +9100,8 @@ static int iwl4965_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e
/* Default value; 4 EDCA QOS priorities */ /* Default value; 4 EDCA QOS priorities */
hw->queues = 4; hw->queues = 4;
#ifdef CONFIG_IWL4965_HT #ifdef CONFIG_IWL4965_HT
#ifdef CONFIG_IWL4965_HT_AGG
/* Enhanced value; more queues, to support 11n aggregation */ /* Enhanced value; more queues, to support 11n aggregation */
hw->queues = 16; hw->queues = 16;
#endif /* CONFIG_IWL4965_HT_AGG */
#endif /* CONFIG_IWL4965_HT */ #endif /* CONFIG_IWL4965_HT */
spin_lock_init(&priv->lock); spin_lock_init(&priv->lock);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment