From 5a3348f06558026c5c842112b83674e00903064c Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Wed, 5 Oct 2011 09:29:01 +0200 Subject: cw1200: AP PS refactoring. * buffered_multicasts_lock was renamed to ps_state_lock. Previous name was quite confusive. * Per-STA rx_queue was created for early RX-ed frames. Not that we really need these frames, but PM status they are holding is important. * priv->tx_suspend_mask was removed, driver is not using it. It was intended for UAPSD and is not needed in current implementation on mac80211. * Fix: cw1200_queue_unlock() was not called from cw1200_queue_clear() when queue was internally locked. ST-Ericsson ID: 360749 Change-Id: I61346db485d34f761d80af786b716d8c73e8b600 Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33541 Reviewed-by: Bartosz MARKOWSKI Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/ap.c | 186 ++++++++++++++++++++------------------------ 1 file changed, 84 insertions(+), 102 deletions(-) (limited to 'drivers/staging/cw1200/ap.c') diff --git a/drivers/staging/cw1200/ap.c b/drivers/staging/cw1200/ap.c index 5e7177f85cf..5a75ac508d1 100755 --- a/drivers/staging/cw1200/ap.c +++ b/drivers/staging/cw1200/ap.c @@ -29,6 +29,10 @@ static int cw1200_start_ap(struct cw1200_common *priv); static int cw1200_update_beaconing(struct cw1200_common *priv); static int cw1200_enable_beaconing(struct cw1200_common *priv, bool enable); +static void __cw1200_sta_notify(struct ieee80211_hw *dev, + struct ieee80211_vif *vif, + enum sta_notify_cmd notify_cmd, + struct ieee80211_sta *sta); /* ******************************************************************** */ /* AP API */ @@ -39,22 +43,26 @@ int cw1200_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct cw1200_common *priv = hw->priv; struct cw1200_sta_priv *sta_priv = (struct cw1200_sta_priv *)&sta->drv_priv; + struct cw1200_link_entry *entry; + struct sk_buff *skb; if (priv->mode != NL80211_IFTYPE_AP) return 0; sta_priv->link_id = cw1200_find_link_id(priv, sta->addr); - if (!sta_priv->link_id) { + if (WARN_ON(!sta_priv->link_id)) { + /* Impossible error */ wiphy_info(priv->hw->wiphy, "[AP] No more link IDs available.\n"); return -ENOENT; } - spin_lock_bh(&priv->buffered_multicasts_lock); - priv->link_id_db[sta_priv->link_id - 1].status = CW1200_LINK_HARD; - if (priv->link_id_db[sta_priv->link_id - 1].ps) - ieee80211_sta_ps_transition(sta, true); - spin_unlock_bh(&priv->buffered_multicasts_lock); + entry = &priv->link_id_db[sta_priv->link_id - 1]; + spin_lock_bh(&priv->ps_state_lock); + entry->status = CW1200_LINK_HARD; + while ((skb = skb_dequeue(&entry->rx_queue))) + ieee80211_rx_irqsafe(priv->hw, skb); + spin_unlock_bh(&priv->ps_state_lock); return 0; } @@ -64,19 +72,24 @@ int cw1200_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct cw1200_common *priv = hw->priv; struct cw1200_sta_priv *sta_priv = (struct cw1200_sta_priv *)&sta->drv_priv; + struct cw1200_link_entry *entry; if (priv->mode != NL80211_IFTYPE_AP || !sta_priv->link_id) return 0; - spin_lock_bh(&priv->buffered_multicasts_lock); - priv->link_id_db[sta_priv->link_id - 1].status = CW1200_LINK_SOFT; - priv->link_id_db[sta_priv->link_id - 1].timestamp = jiffies; + /* HACK! To be removed when accurate TX ststus + * reporting for dropped frames is implemented. */ + ieee80211_sta_eosp_irqsafe(sta); + + entry = &priv->link_id_db[sta_priv->link_id - 1]; + spin_lock_bh(&priv->ps_state_lock); + entry->status = CW1200_LINK_SOFT; + entry->timestamp = jiffies; if (!delayed_work_pending(&priv->link_id_gc_work)) queue_delayed_work(priv->workqueue, &priv->link_id_gc_work, CW1200_LINK_ID_GC_TIMEOUT); - priv->link_id_db[sta_priv->link_id - 1].ps = false; - spin_unlock_bh(&priv->buffered_multicasts_lock); + spin_unlock_bh(&priv->ps_state_lock); return 0; } @@ -89,45 +102,44 @@ static void __cw1200_sta_notify(struct ieee80211_hw *dev, struct cw1200_sta_priv *sta_priv = (struct cw1200_sta_priv *)&sta->drv_priv; u32 bit = BIT(sta_priv->link_id); + u32 prev = priv->sta_asleep_mask & bit; - spin_lock_bh(&priv->buffered_multicasts_lock); switch (notify_cmd) { case STA_NOTIFY_SLEEP: - if (priv->buffered_multicasts && + if (!prev) { + if (priv->buffered_multicasts && !priv->sta_asleep_mask) queue_work(priv->workqueue, &priv->multicast_start_work); - priv->sta_asleep_mask |= bit; + priv->sta_asleep_mask |= bit; + } break; case STA_NOTIFY_AWAKE: - priv->sta_asleep_mask &= ~bit; - if (priv->tx_multicast && - !priv->sta_asleep_mask) - queue_work(priv->workqueue, - &priv->multicast_stop_work); - cw1200_bh_wakeup(priv); + if (prev) { + priv->sta_asleep_mask &= ~bit; + priv->pspoll_mask &= ~bit; + if (priv->tx_multicast && + !priv->sta_asleep_mask) + queue_work(priv->workqueue, + &priv->multicast_stop_work); + cw1200_bh_wakeup(priv); + } break; } - spin_unlock_bh(&priv->buffered_multicasts_lock); } -static void __cw1200_ps_notify(struct cw1200_common *priv, - struct ieee80211_sta *sta, - int link_id, bool ps) +void cw1200_sta_notify(struct ieee80211_hw *dev, + struct ieee80211_vif *vif, + enum sta_notify_cmd notify_cmd, + struct ieee80211_sta *sta) { - txrx_printk(KERN_DEBUG "%s for LinkId: %d. Suspend: %.8X\n", - ps ? "Stop" : "Start", - link_id, priv->tx_suspend_mask[0]); - - priv->link_id_db[link_id - 1].ps = ps; - if (sta) { - __cw1200_sta_notify(priv->hw, priv->vif, - ps ? STA_NOTIFY_SLEEP : STA_NOTIFY_AWAKE, sta); - ieee80211_sta_ps_transition_ni(sta, ps); - } + struct cw1200_common *priv = dev->priv; + spin_lock_bh(&priv->ps_state_lock); + __cw1200_sta_notify(dev, vif, notify_cmd, sta); + spin_unlock_bh(&priv->ps_state_lock); } -void cw1200_ps_notify(struct cw1200_common *priv, +static void cw1200_ps_notify(struct cw1200_common *priv, int link_id, bool ps) { struct ieee80211_sta *sta; @@ -135,13 +147,17 @@ void cw1200_ps_notify(struct cw1200_common *priv, if (!link_id || link_id > CW1200_MAX_STA_IN_AP_MODE) return; - if (!!ps == !!priv->link_id_db[link_id - 1].ps) - return; + txrx_printk(KERN_DEBUG "%s for LinkId: %d. STAs asleep: %.8X\n", + ps ? "Stop" : "Start", + link_id, priv->sta_asleep_mask); rcu_read_lock(); sta = ieee80211_find_sta(priv->vif, priv->link_id_db[link_id - 1].mac); - __cw1200_ps_notify(priv, sta, link_id, ps); + if (sta) { + __cw1200_sta_notify(priv->hw, priv->vif, + ps ? STA_NOTIFY_SLEEP : STA_NOTIFY_AWAKE, sta); + } rcu_read_unlock(); } @@ -594,12 +610,12 @@ void cw1200_mcast_timeout(unsigned long arg) struct cw1200_common *priv = (struct cw1200_common *)arg; - spin_lock_bh(&priv->buffered_multicasts_lock); + spin_lock_bh(&priv->ps_state_lock); priv->tx_multicast = priv->aid0_bit_set && priv->buffered_multicasts; if (priv->tx_multicast) cw1200_bh_wakeup(priv); - spin_unlock_bh(&priv->buffered_multicasts_lock); + spin_unlock_bh(&priv->ps_state_lock); } int cw1200_ampdu_action(struct ieee80211_hw *hw, @@ -620,18 +636,6 @@ int cw1200_ampdu_action(struct ieee80211_hw *hw, void cw1200_suspend_resume(struct cw1200_common *priv, struct wsm_suspend_resume *arg) { - int queue = BIT(wsm_queue_id_to_linux(arg->queue)); - u32 unicast = BIT(arg->link_id); - u32 wakeup_required = 0; - u32 set = 0; - u32 clear; - u32 tx_suspend_mask; - bool cancel_tmo = false; - int i; - - if (!arg->link_id) /* For all links */ - unicast = BIT(CW1200_MAX_STA_IN_AP_MODE + 1) - 2; - /* if () is intendend to protect against spam. FW sends * "start multicast" request on every DTIM. */ if (arg->stop || !arg->multicast || priv->buffered_multicasts) @@ -640,7 +644,8 @@ void cw1200_suspend_resume(struct cw1200_common *priv, arg->multicast ? "broadcast" : "unicast"); if (arg->multicast) { - spin_lock_bh(&priv->buffered_multicasts_lock); + bool cancel_tmo = false; + spin_lock_bh(&priv->ps_state_lock); if (arg->stop) { priv->tx_multicast = false; } else { @@ -658,44 +663,19 @@ void cw1200_suspend_resume(struct cw1200_common *priv, cw1200_bh_wakeup(priv); } } - spin_unlock_bh(&priv->buffered_multicasts_lock); + spin_unlock_bh(&priv->ps_state_lock); if (cancel_tmo) del_timer_sync(&priv->mcast_timeout); } else { - if (arg->stop) - set = unicast; - else - set = 0; - - clear = set ^ unicast; - - /* TODO: if (!priv->uapsd) */ - queue = 0x0F; - - spin_lock_bh(&priv->buffered_multicasts_lock); - priv->sta_asleep_mask |= set; - for (i = 0; i < 4; ++i) { - if (!(queue & BIT(i))) - continue; - - tx_suspend_mask = priv->tx_suspend_mask[i]; - priv->tx_suspend_mask[i] = - (tx_suspend_mask & ~clear) | set; - - wakeup_required = wakeup_required || - cw1200_queue_get_num_queued( - &priv->tx_queue[i], - tx_suspend_mask & clear); - } - spin_unlock_bh(&priv->buffered_multicasts_lock); + spin_lock_bh(&priv->ps_state_lock); cw1200_ps_notify(priv, arg->link_id, arg->stop); + spin_unlock_bh(&priv->ps_state_lock); + if (!arg->stop) + cw1200_bh_wakeup(priv); } - if (wakeup_required) - cw1200_bh_wakeup(priv); return; } - /* ******************************************************************** */ /* AP privates */ @@ -873,7 +853,7 @@ static int cw1200_update_beaconing(struct cw1200_common *priv) int cw1200_find_link_id(struct cw1200_common *priv, const u8 *mac) { int i, ret = 0; - spin_lock_bh(&priv->buffered_multicasts_lock); + spin_lock_bh(&priv->ps_state_lock); for (i = 0; i < CW1200_MAX_STA_IN_AP_MODE; ++i) { if (!memcmp(mac, priv->link_id_db[i].mac, ETH_ALEN) && priv->link_id_db[i].status) { @@ -882,7 +862,7 @@ int cw1200_find_link_id(struct cw1200_common *priv, const u8 *mac) break; } } - spin_unlock_bh(&priv->buffered_multicasts_lock); + spin_unlock_bh(&priv->ps_state_lock); return ret; } @@ -892,7 +872,7 @@ int cw1200_alloc_link_id(struct cw1200_common *priv, const u8 *mac) unsigned long max_inactivity = 0; unsigned long now = jiffies; - spin_lock_bh(&priv->buffered_multicasts_lock); + spin_lock_bh(&priv->ps_state_lock); for (i = 0; i < CW1200_MAX_STA_IN_AP_MODE; ++i) { if (!priv->link_id_db[i].status) { ret = i + 1; @@ -909,11 +889,13 @@ int cw1200_alloc_link_id(struct cw1200_common *priv, const u8 *mac) } } if (ret) { + struct cw1200_link_entry *entry = &priv->link_id_db[ret - 1]; ap_printk(KERN_DEBUG "[AP] STA added, link_id: %d\n", ret); - priv->link_id_db[ret - 1].status = CW1200_LINK_RESERVE; - memcpy(&priv->link_id_db[ret - 1].mac, mac, ETH_ALEN); - memset(&priv->link_id_db[ret - 1].buffered, 0, CW1200_MAX_TID); + entry->status = CW1200_LINK_RESERVE; + memcpy(&entry->mac, mac, ETH_ALEN); + memset(&entry->buffered, 0, CW1200_MAX_TID); + skb_queue_head_init(&entry->rx_queue); wsm_lock_tx_async(priv); if (queue_work(priv->workqueue, &priv->link_id_work) <= 0) wsm_unlock_tx(priv); @@ -922,7 +904,7 @@ int cw1200_alloc_link_id(struct cw1200_common *priv, const u8 *mac) "[AP] Early: no more link IDs available.\n"); } - spin_unlock_bh(&priv->buffered_multicasts_lock); + spin_unlock_bh(&priv->ps_state_lock); return ret; } @@ -950,13 +932,13 @@ void cw1200_link_id_gc_work(struct work_struct *work) long ttl; bool need_reset; u32 mask; - int i, j; + int i; if (priv->join_status != CW1200_JOIN_STATUS_AP) return; wsm_lock_tx(priv); - spin_lock_bh(&priv->buffered_multicasts_lock); + spin_lock_bh(&priv->ps_state_lock); for (i = 0; i < CW1200_MAX_STA_IN_AP_MODE; ++i) { need_reset = false; mask = BIT(i + 1); @@ -965,8 +947,7 @@ void cw1200_link_id_gc_work(struct work_struct *work) !(priv->link_id_map & mask))) { if (priv->link_id_map & mask) { priv->sta_asleep_mask &= ~mask; - for (j = 0; j < 4; ++j) - priv->tx_suspend_mask[j] &= ~mask; + priv->pspoll_mask &= ~mask; need_reset = true; } priv->link_id_map |= mask; @@ -974,7 +955,7 @@ void cw1200_link_id_gc_work(struct work_struct *work) priv->link_id_db[i].status = CW1200_LINK_SOFT; memcpy(map_link.mac_addr, priv->link_id_db[i].mac, ETH_ALEN); - spin_unlock_bh(&priv->buffered_multicasts_lock); + spin_unlock_bh(&priv->ps_state_lock); if (need_reset) { reset.link_id = i + 1; WARN_ON(wsm_reset(priv, &reset)); @@ -982,7 +963,7 @@ void cw1200_link_id_gc_work(struct work_struct *work) map_link.link_id = i + 1; WARN_ON(wsm_map_link(priv, &map_link)); next_gc = min(next_gc, CW1200_LINK_ID_GC_TIMEOUT); - spin_lock_bh(&priv->buffered_multicasts_lock); + spin_lock_bh(&priv->ps_state_lock); } else if (priv->link_id_db[i].status == CW1200_LINK_SOFT) { ttl = priv->link_id_db[i].timestamp - now + CW1200_LINK_ID_GC_TIMEOUT; @@ -991,22 +972,23 @@ void cw1200_link_id_gc_work(struct work_struct *work) priv->link_id_db[i].status = CW1200_LINK_OFF; priv->link_id_map &= ~mask; priv->sta_asleep_mask &= ~mask; - for (j = 0; j < 4; ++j) - priv->tx_suspend_mask[j] &= ~mask; + priv->pspoll_mask &= ~mask; memset(map_link.mac_addr, 0, ETH_ALEN); - spin_unlock_bh(&priv->buffered_multicasts_lock); + spin_unlock_bh(&priv->ps_state_lock); reset.link_id = i + 1; WARN_ON(wsm_reset(priv, &reset)); - spin_lock_bh(&priv->buffered_multicasts_lock); + spin_lock_bh(&priv->ps_state_lock); } else { next_gc = min(next_gc, (unsigned long)ttl); } } - if (need_reset) + if (need_reset) { + skb_queue_purge(&priv->link_id_db[i].rx_queue); ap_printk(KERN_DEBUG "[AP] STA removed, link_id: %d\n", reset.link_id); + } } - spin_unlock_bh(&priv->buffered_multicasts_lock); + spin_unlock_bh(&priv->ps_state_lock); if (next_gc != -1) queue_delayed_work(priv->workqueue, &priv->link_id_gc_work, next_gc); -- cgit v1.2.3