@@ -131,6 +131,7 @@ struct ar9170_rxstream_mpdu_merge {
enum ar9170_tid_state {
AR9170_TID_STATE_INVALID,
AR9170_TID_STATE_SHUTDOWN,
+ AR9170_TID_STATE_STARTING,
AR9170_TID_STATE_PROGRESS,
AR9170_TID_STATE_COMPLETE,
};
@@ -233,6 +234,10 @@ struct ar9170 {
struct list_head tx_ampdu_list;
unsigned int tx_ampdu_pending;
+ spinlock_t addba_list_lock;
+ struct list_head addba_list;
+ struct work_struct start_tx_ba_work;
+
/* rxstream mpdu merge */
struct ar9170_rxstream_mpdu_merge rx_mpdu;
struct sk_buff *rx_failover;
@@ -246,6 +251,12 @@ struct ar9170 {
struct ar9170_sta_info {
struct ar9170_sta_tid agg[AR9170_NUM_TID];
unsigned int ampdu_max_len;
+
+#ifndef AR9170_MAC80211_RC_MCS
+ unsigned int max_mcs;
+ int score;
+ unsigned int current_rate;
+#endif /* AR9170_MAC80211_RC_MCS */
};
#define AR9170_TX_FLAG_WAIT_FOR_ACK BIT(0)
@@ -49,7 +49,7 @@ static int modparam_nohwcrypt;
module_param_named(nohwcrypt, modparam_nohwcrypt, bool, S_IRUGO);
MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption.");
-static int modparam_ht;
+static int modparam_ht = 1;
module_param_named(ht, modparam_ht, bool, S_IRUGO);
MODULE_PARM_DESC(ht, "enable MPDU aggregation.");
@@ -285,6 +285,124 @@ static void ar9170_dump_tx_status_ampdu(struct ar9170 *ar)
#endif /* AR9170_TXAGG_DEBUG */
+static void ar9170_setup_aggregation(struct ar9170 *ar, struct sk_buff *skb)
+{
+ unsigned long flags;
+ struct ieee80211_sta *sta;
+ struct ieee80211_hdr *hdr = (void *) skb->data;
+ struct ar9170_sta_tid *tid_info;
+ u16 tid = ar9170_get_tid(skb);
+
+ if (!conf_is_ht(&ar->hw->conf))
+ return ;
+
+ /* don't start aggregation for non-qos / WPA handshake */
+ if ((skb->protocol == cpu_to_le16(ETH_P_PAE)) ||
+ !ieee80211_is_data_qos(hdr->frame_control))
+ return ;
+
+ rcu_read_lock();
+ sta = ieee80211_find_sta(ar->hw, ieee80211_get_DA(hdr));
+ if (!sta)
+ goto out_unlock;
+
+ if (!sta->ht_cap.ht_supported)
+ goto out_unlock;
+
+ tid_info = &((struct ar9170_sta_info *) sta->drv_priv)->agg[tid];
+
+ if (tid_info->retry++ > AR9170_NUM_MAX_BA_RETRY) {
+#ifdef AR9170_TXAGG_DEBUG
+ printk(KERN_DEBUG "%s: too many addba retries for "
+ "ESS:[%pM], tid:%d.\n", wiphy_name(ar->hw->wiphy),
+ sta->addr, tid);
+#endif /* AR9170_TXAGG_DEBUG */
+ goto out_unlock;
+ }
+
+ if (tid_info->state == AR9170_TID_STATE_SHUTDOWN) {
+ if (!list_empty(&tid_info->list)) {
+#ifdef AR9170_TXAGG_DEBUG
+ printk(KERN_DEBUG "%s: addba already queued.\n",
+ wiphy_name(ar->hw->wiphy));
+#endif /* AR9170_TXAGG_DEBUG */
+ goto out_unlock;
+ }
+
+ /* FIXME: no idea if this is right... */
+ tid_info->ssn = GET_NEXT_SEQ_FROM_SKB(skb);
+
+ spin_lock_irqsave(&ar->addba_list_lock, flags);
+ memcpy(tid_info->addr, sta->addr, ETH_ALEN);
+ tid_info->state = AR9170_TID_STATE_STARTING;
+ list_add_tail(&tid_info->list, &ar->addba_list);
+ spin_unlock_irqrestore(&ar->addba_list_lock, flags);
+ ieee80211_queue_work(ar->hw, &ar->start_tx_ba_work);
+ }
+
+out_unlock:
+ rcu_read_unlock();
+}
+
+#ifndef AR9170_MAC80211_RC_MCS
+static void ar9170_feedback_11nrate(struct ar9170 *ar, struct sk_buff *skb)
+{
+ struct ieee80211_tx_info *txinfo = IEEE80211_SKB_CB(skb);
+ struct ieee80211_tx_rate *txrate = txinfo->status.rates;
+ struct ieee80211_sta *sta;
+ struct ar9170_sta_info *sta_info;
+
+ rcu_read_lock();
+ sta = ieee80211_find_sta(ar->hw, ieee80211_get_DA((void *)skb->data));
+ if (sta == NULL)
+ goto out_unlock;
+
+ sta_info = (void *) sta->drv_priv;
+ sta_info->score = (sta_info->score * 99998) / 99999;
+
+ if (txinfo->flags & IEEE80211_TX_STAT_ACK)
+ sta_info->score += 16 - txrate->idx;
+ else
+ sta_info->score -= 16 - txrate->idx;
+
+ if (sta_info->score < 0) {
+ if (sta_info->current_rate)
+ sta_info->current_rate--;
+ } else if (sta_info->score > (1 << txrate->idx) &&
+ sta_info->current_rate < sta_info->max_mcs)
+ sta_info->current_rate++;
+
+out_unlock:
+ rcu_read_unlock();
+}
+
+static void ar9170_select_11nrate(struct ar9170 *ar,
+ struct ieee80211_tx_rate *rate,
+ struct ieee80211_tx_info *info)
+{
+ struct ieee80211_sta *sta = info->control.sta;
+ struct ar9170_sta_info *sta_info = (void *) sta->drv_priv;
+
+ info->flags &= ~IEEE80211_TX_INTFL_RCALGO;
+ rate->flags = IEEE80211_TX_RC_MCS;
+ rate->idx = sta_info->current_rate;
+
+ if ((rate->idx > 4) &&
+ conf_is_ht40(&ar->hw->conf) &&
+ (sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40))
+ rate->flags |= IEEE80211_TX_RC_40_MHZ_WIDTH;
+
+ if ((rate->idx == (sta_info->max_mcs + 1)) &&
+ (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) &&
+ (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40))
+ rate->flags |= IEEE80211_TX_RC_SHORT_GI;
+#ifdef AR9170_RC_MCS_DEBUG
+ if (net_ratelimit())
+ printk(KERN_DEBUG "rate:%x flags:%x\n", rate->idx, rate->flags);
+#endif
+}
+#endif /* AR9170_MAC80211_RC_MCS */
+
/* caller must guarantee exclusive access for _bin_ queue. */
static void ar9170_recycle_expired(struct ar9170 *ar,
struct sk_buff_head *queue,
@@ -353,6 +471,9 @@ static void ar9170_tx_status(struct ar9170 *ar, struct sk_buff *skb,
txinfo->status.rates[0].count = retries + 1;
skb_pull(skb, sizeof(struct ar9170_tx_control));
+ if (tx_status != AR9170_TX_STATUS_FAILED)
+ ar9170_setup_aggregation(ar, skb);
+
ieee80211_tx_status_irqsafe(ar->hw, skb);
}
@@ -388,6 +509,9 @@ static void ar9170_tx_fake_ampdu_status(struct ar9170 *ar)
txinfo->status.rates[0].count = 1;
skb_pull(skb, sizeof(struct ar9170_tx_control));
+#ifndef AR9170_MAC80211_RC_MCS
+ ar9170_feedback_11nrate(ar, skb);
+#endif /* AR9170_MAC80211_RC_MCS */
ieee80211_tx_status_irqsafe(ar->hw, skb);
}
@@ -542,6 +666,9 @@ static void ar9170_handle_block_ack(struct ar9170 *ar, u16 count, u16 r)
txinfo->status.rates[0].count = 1;
skb_pull(skb, sizeof(struct ar9170_tx_control));
+#ifndef AR9170_MAC80211_RC_MCS
+ ar9170_feedback_11nrate(ar, skb);
+#endif /* AR9170_MAC80211_RC_MCS */
ieee80211_tx_status_irqsafe(ar->hw, skb);
count--;
}
@@ -1295,6 +1422,7 @@ static void ar9170_op_stop(struct ieee80211_hw *hw)
cancel_delayed_work_sync(&ar->led_work);
#endif
cancel_work_sync(&ar->beacon_work);
+ cancel_work_sync(&ar->start_tx_ba_work);
mutex_lock(&ar->mutex);
@@ -1414,6 +1542,10 @@ static int ar9170_tx_prepare(struct ar9170 *ar, struct sk_buff *skb)
txc->mac_control |= cpu_to_le16(AR9170_TX_MAC_AGGR);
arinfo->flags = AR9170_TX_FLAG_BLOCK_ACK;
+#ifndef AR9170_MAC80211_RC_MCS
+ ar9170_select_11nrate(ar, txrate, info);
+#endif /* AR9170_MAC80211_RC_MCS */
+
goto out;
}
@@ -1475,6 +1607,13 @@ static void ar9170_tx_prepare_phy(struct ar9170 *ar, struct sk_buff *skb)
u32 r = txrate->idx;
u8 *txpower;
+#ifndef AR9170_MAC80211_RC_MCS
+ if (ar->eeprom.tx_mask == 1)
+ txrate->idx = r = min_t(s8, txrate->idx, 7);
+ else
+ txrate->idx = r = min_t(s8, txrate->idx, 15);
+#endif /* AR9170_MAC80211_RC_MCS */
+
/* heavy clip control */
txc->phy_control |= cpu_to_le32((r & 0x7) << 7);
@@ -1540,6 +1679,7 @@ static void ar9170_tx_prepare_phy(struct ar9170 *ar, struct sk_buff *skb)
/* >= 36M legacy OFDM - use only one chain */
if (rate && rate->bitrate >= 360)
chains = AR9170_TX_PHY_TXCHAIN_1;
+
}
txc->phy_control |= cpu_to_le32(chains << AR9170_TX_PHY_TXCHAIN_SHIFT);
}
@@ -2363,6 +2503,18 @@ static void ar9170_sta_notify(struct ieee80211_hw *hw,
}
sta_info->ampdu_max_len = 1 << (3 + sta->ht_cap.ampdu_factor);
+
+#ifndef AR9170_MAC80211_RC_MCS
+ {
+ unsigned int tx_mcs;
+ unsigned int rx_mcs;
+
+ tx_mcs = ar->eeprom.tx_mask == 1 ? 7 : 15;
+ rx_mcs = find_last_bit((unsigned long *)sta->ht_cap.mcs.rx_mask, 16);
+
+ sta_info->max_mcs = min(tx_mcs, rx_mcs);
+ }
+#endif /* AR9170_MAC80211_RC_MCS */
break;
case STA_NOTIFY_REMOVE:
@@ -2445,7 +2597,7 @@ static int ar9170_ampdu_action(struct ieee80211_hw *hw,
switch (action) {
case IEEE80211_AMPDU_TX_START:
spin_lock_irqsave(&ar->tx_ampdu_list_lock, flags);
- if (tid_info->state != AR9170_TID_STATE_SHUTDOWN ||
+ if (tid_info->state != AR9170_TID_STATE_STARTING ||
!list_empty(&tid_info->list)) {
spin_unlock_irqrestore(&ar->tx_ampdu_list_lock, flags);
#ifdef AR9170_TXAGG_DEBUG
@@ -2514,6 +2666,35 @@ static const struct ieee80211_ops ar9170_ops = {
.ampdu_action = ar9170_ampdu_action,
};
+static void ar9170_start_tx_ba(struct work_struct *work)
+{
+ struct ar9170 *ar = container_of(work, struct ar9170,
+ start_tx_ba_work);
+ unsigned long flags;
+
+ while (!list_empty(&ar->addba_list)) {
+ struct ar9170_sta_tid *tid_info;
+
+ spin_lock_irqsave(&ar->addba_list_lock, flags);
+ tid_info = list_first_entry(&ar->addba_list,
+ struct ar9170_sta_tid, list);
+ if (tid_info) {
+ tid_info->retry = 0;
+ list_del_init(&tid_info->list);
+
+ if (tid_info->state != AR9170_TID_STATE_STARTING) {
+ WARN_ON(1);
+ tid_info = NULL;
+ }
+ }
+ spin_unlock_irqrestore(&ar->addba_list_lock, flags);
+
+ if (tid_info)
+ ieee80211_start_tx_ba_session(ar->hw, tid_info->addr,
+ tid_info->tid);
+ }
+}
+
void *ar9170_alloc(size_t priv_size)
{
struct ieee80211_hw *hw;
@@ -2542,6 +2723,7 @@ void *ar9170_alloc(size_t priv_size)
mutex_init(&ar->mutex);
spin_lock_init(&ar->cmdlock);
spin_lock_init(&ar->tx_stats_lock);
+ spin_lock_init(&ar->addba_list_lock);
spin_lock_init(&ar->tx_ampdu_list_lock);
skb_queue_head_init(&ar->tx_status_ampdu);
for (i = 0; i < __AR9170_NUM_TXQ; i++) {
@@ -2550,8 +2732,10 @@ void *ar9170_alloc(size_t priv_size)
}
ar9170_rx_reset_rx_mpdu(ar);
INIT_WORK(&ar->beacon_work, ar9170_new_beacon);
+ INIT_WORK(&ar->start_tx_ba_work, ar9170_start_tx_ba);
INIT_DELAYED_WORK(&ar->tx_janitor, ar9170_tx_janitor);
INIT_LIST_HEAD(&ar->tx_ampdu_list);
+ INIT_LIST_HEAD(&ar->addba_list);
/* all hw supports 2.4 GHz, so set channel to 1 by default */
ar->channel = &ar9170_2ghz_chantable[0];