struct carl9170_sta_info *sta_info = (void *) sta->drv_priv;
unsigned int i;
+ atomic_set(&sta_info->pending_frames, 0);
+
if (sta->ht_cap.ht_supported) {
if (sta->ht_cap.ampdu_density > 6) {
/*
enum sta_notify_cmd cmd,
struct ieee80211_sta *sta)
{
- struct ar9170 *ar = hw->priv;
struct carl9170_sta_info *sta_info = (void *) sta->drv_priv;
- struct sk_buff *skb, *tmp;
- struct sk_buff_head free;
- int i;
switch (cmd) {
case STA_NOTIFY_SLEEP:
- /*
- * Since the peer is no longer listening, we have to return
- * as many SKBs as possible back to the mac80211 stack.
- * It will deal with the retry procedure, once the peer
- * has become available again.
- *
- * NB: Ideally, the driver should return the all frames in
- * the correct, ascending order. However, I think that this
- * functionality should be implemented in the stack and not
- * here...
- */
-
- __skb_queue_head_init(&free);
-
- if (sta->ht_cap.ht_supported) {
- rcu_read_lock();
- for (i = 0; i < CARL9170_NUM_TID; i++) {
- struct carl9170_sta_tid *tid_info;
-
- tid_info = rcu_dereference(sta_info->agg[i]);
-
- if (!tid_info)
- continue;
-
- spin_lock_bh(&ar->tx_ampdu_list_lock);
- if (tid_info->state >
- CARL9170_TID_STATE_SUSPEND)
- tid_info->state =
- CARL9170_TID_STATE_SUSPEND;
- spin_unlock_bh(&ar->tx_ampdu_list_lock);
-
- spin_lock_bh(&tid_info->lock);
- while ((skb = __skb_dequeue(&tid_info->queue)))
- __skb_queue_tail(&free, skb);
- spin_unlock_bh(&tid_info->lock);
- }
- rcu_read_unlock();
- }
-
- for (i = 0; i < ar->hw->queues; i++) {
- spin_lock_bh(&ar->tx_pending[i].lock);
- skb_queue_walk_safe(&ar->tx_pending[i], skb, tmp) {
- struct _carl9170_tx_superframe *super;
- struct ieee80211_hdr *hdr;
- struct ieee80211_tx_info *info;
-
- super = (void *) skb->data;
- hdr = (void *) super->frame_data;
-
- if (compare_ether_addr(hdr->addr1, sta->addr))
- continue;
-
- __skb_unlink(skb, &ar->tx_pending[i]);
-
- info = IEEE80211_SKB_CB(skb);
- if (info->flags & IEEE80211_TX_CTL_AMPDU)
- atomic_dec(&ar->tx_ampdu_upload);
-
- carl9170_tx_status(ar, skb, false);
- }
- spin_unlock_bh(&ar->tx_pending[i].lock);
- }
-
- while ((skb = __skb_dequeue(&free)))
- carl9170_tx_status(ar, skb, false);
-
+ sta_info->sleeping = true;
+ if (atomic_read(&sta_info->pending_frames))
+ ieee80211_sta_block_awake(hw, sta, true);
break;
case STA_NOTIFY_AWAKE:
- if (!sta->ht_cap.ht_supported)
- return;
-
- rcu_read_lock();
- for (i = 0; i < CARL9170_NUM_TID; i++) {
- struct carl9170_sta_tid *tid_info;
-
- tid_info = rcu_dereference(sta_info->agg[i]);
-
- if (!tid_info)
- continue;
-
- if ((tid_info->state == CARL9170_TID_STATE_SUSPEND))
- tid_info->state = CARL9170_TID_STATE_IDLE;
- }
- rcu_read_unlock();
+ sta_info->sleeping = false;
break;
}
}
spin_unlock_bh(&ar->tx_stats_lock);
}
+/* needs rcu_read_lock */
+static struct ieee80211_sta *__carl9170_get_tx_sta(struct ar9170 *ar,
+ struct sk_buff *skb)
+{
+ struct _carl9170_tx_superframe *super = (void *) skb->data;
+ struct ieee80211_hdr *hdr = (void *) super->frame_data;
+ struct ieee80211_vif *vif;
+ unsigned int vif_id;
+
+ vif_id = (super->s.misc & CARL9170_TX_SUPER_MISC_VIF_ID) >>
+ CARL9170_TX_SUPER_MISC_VIF_ID_S;
+
+ if (WARN_ON_ONCE(vif_id >= AR9170_MAX_VIRTUAL_MAC))
+ return NULL;
+
+ vif = rcu_dereference(ar->vif_priv[vif_id].vif);
+ if (unlikely(!vif))
+ return NULL;
+
+ /*
+ * Normally we should use wrappers like ieee80211_get_DA to get
+ * the correct peer ieee80211_sta.
+ *
+ * But there is a problem with indirect traffic (broadcasts, or
+ * data which is designated for other stations) in station mode.
+ * The frame will be directed to the AP for distribution and not
+ * to the actual destination.
+ */
+
+ return ieee80211_find_sta(vif, hdr->addr1);
+}
+
+static void carl9170_tx_ps_unblock(struct ar9170 *ar, struct sk_buff *skb)
+{
+ struct ieee80211_sta *sta;
+ struct carl9170_sta_info *sta_info;
+
+ rcu_read_lock();
+ sta = __carl9170_get_tx_sta(ar, skb);
+ if (unlikely(!sta))
+ goto out_rcu;
+
+ sta_info = (struct carl9170_sta_info *) sta->drv_priv;
+ if (atomic_dec_return(&sta_info->pending_frames) == 0)
+ ieee80211_sta_block_awake(ar->hw, sta, false);
+
+out_rcu:
+ rcu_read_unlock();
+}
+
static void carl9170_tx_accounting_free(struct ar9170 *ar, struct sk_buff *skb)
{
struct ieee80211_tx_info *txinfo;
}
spin_unlock_bh(&ar->tx_stats_lock);
+
if (atomic_dec_and_test(&ar->tx_total_queued))
complete(&ar->tx_flush);
}
{
struct _carl9170_tx_superframe *super = (void *) skb->data;
struct ieee80211_hdr *hdr = (void *) super->frame_data;
- struct ieee80211_tx_info *tx_info;
struct carl9170_tx_info *ar_info;
- struct carl9170_sta_info *sta_info;
struct ieee80211_sta *sta;
+ struct carl9170_sta_info *sta_info;
struct carl9170_sta_tid *tid_info;
- struct ieee80211_vif *vif;
- unsigned int vif_id;
u8 tid;
if (!(txinfo->flags & IEEE80211_TX_CTL_AMPDU) ||
(!(super->f.mac_control & cpu_to_le16(AR9170_TX_MAC_AGGR))))
return;
- tx_info = IEEE80211_SKB_CB(skb);
- ar_info = (void *) tx_info->rate_driver_data;
-
- vif_id = (super->s.misc & CARL9170_TX_SUPER_MISC_VIF_ID) >>
- CARL9170_TX_SUPER_MISC_VIF_ID_S;
-
- if (WARN_ON_ONCE(vif_id >= AR9170_MAX_VIRTUAL_MAC))
- return;
+ ar_info = (void *) txinfo->rate_driver_data;
rcu_read_lock();
- vif = rcu_dereference(ar->vif_priv[vif_id].vif);
- if (unlikely(!vif))
- goto out_rcu;
-
- /*
- * Normally we should use wrappers like ieee80211_get_DA to get
- * the correct peer ieee80211_sta.
- *
- * But there is a problem with indirect traffic (broadcasts, or
- * data which is designated for other stations) in station mode.
- * The frame will be directed to the AP for distribution and not
- * to the actual destination.
- */
- sta = ieee80211_find_sta(vif, hdr->addr1);
+ sta = __carl9170_get_tx_sta(ar, skb);
if (unlikely(!sta))
goto out_rcu;
if (txinfo->flags & IEEE80211_TX_CTL_AMPDU)
carl9170_tx_status_process_ampdu(ar, skb, txinfo);
+ carl9170_tx_ps_unblock(ar, skb);
carl9170_tx_put_skb(skb);
}
struct sk_buff *skb;
struct ieee80211_tx_info *txinfo;
struct carl9170_tx_info *arinfo;
- struct _carl9170_tx_superframe *super;
struct ieee80211_sta *sta;
- struct ieee80211_vif *vif;
- struct ieee80211_hdr *hdr;
- unsigned int vif_id;
rcu_read_lock();
list_for_each_entry_rcu(iter, &ar->tx_ampdu_list, list) {
msecs_to_jiffies(CARL9170_QUEUE_TIMEOUT)))
goto unlock;
- super = (void *) skb->data;
- hdr = (void *) super->frame_data;
-
- vif_id = (super->s.misc & CARL9170_TX_SUPER_MISC_VIF_ID) >>
- CARL9170_TX_SUPER_MISC_VIF_ID_S;
-
- if (WARN_ON(vif_id >= AR9170_MAX_VIRTUAL_MAC))
- goto unlock;
-
- vif = rcu_dereference(ar->vif_priv[vif_id].vif);
- if (WARN_ON(!vif))
- goto unlock;
-
- sta = ieee80211_find_sta(vif, hdr->addr1);
+ sta = __carl9170_get_tx_sta(ar, skb);
if (WARN_ON(!sta))
goto unlock;
arinfo = (void *) info->rate_driver_data;
arinfo->timeout = jiffies;
-
- /*
- * increase ref count to "2".
- * Ref counting is the easiest way to solve the race between
- * the the urb's completion routine: carl9170_tx_callback and
- * wlan tx status functions: carl9170_tx_status/janitor.
- */
- carl9170_tx_get_skb(skb);
-
return skb;
err_unlock:
__carl9170_tx_process_status(ar, super->s.cookie, q);
}
+static bool carl9170_tx_ps_drop(struct ar9170 *ar, struct sk_buff *skb)
+{
+ struct ieee80211_sta *sta;
+ struct carl9170_sta_info *sta_info;
+
+ rcu_read_lock();
+ sta = __carl9170_get_tx_sta(ar, skb);
+ if (!sta)
+ goto out_rcu;
+
+ sta_info = (void *) sta->drv_priv;
+ if (unlikely(sta_info->sleeping)) {
+ struct ieee80211_tx_info *tx_info;
+
+ rcu_read_unlock();
+
+ tx_info = IEEE80211_SKB_CB(skb);
+ if (tx_info->flags & IEEE80211_TX_CTL_AMPDU)
+ atomic_dec(&ar->tx_ampdu_upload);
+
+ tx_info->flags |= IEEE80211_TX_STAT_TX_FILTERED;
+ carl9170_tx_status(ar, skb, false);
+ return true;
+ }
+
+out_rcu:
+ rcu_read_unlock();
+ return false;
+}
+
static void carl9170_tx(struct ar9170 *ar)
{
struct sk_buff *skb;
if (unlikely(!skb))
break;
+ if (unlikely(carl9170_tx_ps_drop(ar, skb)))
+ continue;
+
atomic_inc(&ar->tx_total_pending);
q = __carl9170_get_queue(ar, i);
*/
skb_queue_tail(&ar->tx_status[q], skb);
+ /*
+ * increase ref count to "2".
+ * Ref counting is the easiest way to solve the
+ * race between the urb's completion routine:
+ * carl9170_tx_callback
+ * and wlan tx status functions:
+ * carl9170_tx_status/janitor.
+ */
+ carl9170_tx_get_skb(skb);
+
carl9170_usb_tx(ar, skb);
schedule_garbagecollector = true;
}
* all ressouces which are associated with the frame.
*/
+ if (sta) {
+ struct carl9170_sta_info *stai = (void *) sta->drv_priv;
+ atomic_inc(&stai->pending_frames);
+ }
+
if (info->flags & IEEE80211_TX_CTL_AMPDU) {
run = carl9170_tx_ampdu_queue(ar, sta, skb);
if (run)