diff options
author | John W. Linville <linville@tuxdriver.com> | 2011-10-11 15:35:42 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2011-10-11 15:35:42 -0400 |
commit | 094daf7db7c47861009899ce23f9177d761e20b0 (patch) | |
tree | a107065393720b80664157a035b206576e834793 /net/mac80211/sta_info.c | |
parent | 3ed6f6958c0ac21958285d8648f14d34da4bbcb3 (diff) | |
parent | 5f68a2b0a890d086e40fc7b55f4a0c32c28bc0d2 (diff) | |
download | blackbird-obmc-linux-094daf7db7c47861009899ce23f9177d761e20b0.tar.gz blackbird-obmc-linux-094daf7db7c47861009899ce23f9177d761e20b0.zip |
Merge branch 'master' of git://git.infradead.org/users/linville/wireless-next into for-davem
Conflicts:
Documentation/feature-removal-schedule.txt
Diffstat (limited to 'net/mac80211/sta_info.c')
-rw-r--r-- | net/mac80211/sta_info.c | 636 |
1 files changed, 515 insertions, 121 deletions
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 0a7e0fed3251..58b1c2bb26d2 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -24,6 +24,7 @@ #include "sta_info.h" #include "debugfs_sta.h" #include "mesh.h" +#include "wme.h" /** * DOC: STA information lifetime rules @@ -243,13 +244,22 @@ static void sta_unblock(struct work_struct *wk) if (sta->dead) return; - if (!test_sta_flags(sta, WLAN_STA_PS_STA)) + if (!test_sta_flag(sta, WLAN_STA_PS_STA)) ieee80211_sta_ps_deliver_wakeup(sta); - else if (test_and_clear_sta_flags(sta, WLAN_STA_PSPOLL)) { - clear_sta_flags(sta, WLAN_STA_PS_DRIVER); + else if (test_and_clear_sta_flag(sta, WLAN_STA_PSPOLL)) { + clear_sta_flag(sta, WLAN_STA_PS_DRIVER); + + local_bh_disable(); ieee80211_sta_ps_deliver_poll_response(sta); + local_bh_enable(); + } else if (test_and_clear_sta_flag(sta, WLAN_STA_UAPSD)) { + clear_sta_flag(sta, WLAN_STA_PS_DRIVER); + + local_bh_disable(); + ieee80211_sta_ps_deliver_uapsd(sta); + local_bh_enable(); } else - clear_sta_flags(sta, WLAN_STA_PS_DRIVER); + clear_sta_flag(sta, WLAN_STA_PS_DRIVER); } static int sta_prepare_rate_control(struct ieee80211_local *local, @@ -282,7 +292,6 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, return NULL; spin_lock_init(&sta->lock); - spin_lock_init(&sta->flaglock); INIT_WORK(&sta->drv_unblock_wk, sta_unblock); INIT_WORK(&sta->ampdu_mlme.work, ieee80211_ba_session_work); mutex_init(&sta->ampdu_mlme.mtx); @@ -309,8 +318,10 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, */ sta->timer_to_tid[i] = i; } - skb_queue_head_init(&sta->ps_tx_buf); - skb_queue_head_init(&sta->tx_filtered); + for (i = 0; i < IEEE80211_NUM_ACS; i++) { + skb_queue_head_init(&sta->ps_tx_buf[i]); + skb_queue_head_init(&sta->tx_filtered[i]); + } for (i = 0; i < NUM_RX_DATA_QUEUES; i++) sta->last_seq_ctrl[i] = cpu_to_le16(USHRT_MAX); @@ -641,54 +652,84 @@ static inline void __bss_tim_clear(struct ieee80211_if_ap *bss, u16 aid) bss->tim[aid / 8] &= ~(1 << (aid % 8)); } -static void __sta_info_set_tim_bit(struct ieee80211_if_ap *bss, - struct sta_info *sta) +static unsigned long ieee80211_tids_for_ac(int ac) { - BUG_ON(!bss); - - __bss_tim_set(bss, sta->sta.aid); - - if (sta->local->ops->set_tim) { - sta->local->tim_in_locked_section = true; - drv_set_tim(sta->local, &sta->sta, true); - sta->local->tim_in_locked_section = false; + /* If we ever support TIDs > 7, this obviously needs to be adjusted */ + switch (ac) { + case IEEE80211_AC_VO: + return BIT(6) | BIT(7); + case IEEE80211_AC_VI: + return BIT(4) | BIT(5); + case IEEE80211_AC_BE: + return BIT(0) | BIT(3); + case IEEE80211_AC_BK: + return BIT(1) | BIT(2); + default: + WARN_ON(1); + return 0; } } -void sta_info_set_tim_bit(struct sta_info *sta) +void sta_info_recalc_tim(struct sta_info *sta) { + struct ieee80211_local *local = sta->local; + struct ieee80211_if_ap *bss = sta->sdata->bss; unsigned long flags; + bool indicate_tim = false; + u8 ignore_for_tim = sta->sta.uapsd_queues; + int ac; - BUG_ON(!sta->sdata->bss); + if (WARN_ON_ONCE(!sta->sdata->bss)) + return; - spin_lock_irqsave(&sta->local->sta_lock, flags); - __sta_info_set_tim_bit(sta->sdata->bss, sta); - spin_unlock_irqrestore(&sta->local->sta_lock, flags); -} + /* No need to do anything if the driver does all */ + if (local->hw.flags & IEEE80211_HW_AP_LINK_PS) + return; -static void __sta_info_clear_tim_bit(struct ieee80211_if_ap *bss, - struct sta_info *sta) -{ - BUG_ON(!bss); + if (sta->dead) + goto done; + + /* + * If all ACs are delivery-enabled then we should build + * the TIM bit for all ACs anyway; if only some are then + * we ignore those and build the TIM bit using only the + * non-enabled ones. + */ + if (ignore_for_tim == BIT(IEEE80211_NUM_ACS) - 1) + ignore_for_tim = 0; + + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { + unsigned long tids; - __bss_tim_clear(bss, sta->sta.aid); + if (ignore_for_tim & BIT(ac)) + continue; + + indicate_tim |= !skb_queue_empty(&sta->tx_filtered[ac]) || + !skb_queue_empty(&sta->ps_tx_buf[ac]); + if (indicate_tim) + break; - if (sta->local->ops->set_tim) { - sta->local->tim_in_locked_section = true; - drv_set_tim(sta->local, &sta->sta, false); - sta->local->tim_in_locked_section = false; + tids = ieee80211_tids_for_ac(ac); + + indicate_tim |= + sta->driver_buffered_tids & tids; } -} -void sta_info_clear_tim_bit(struct sta_info *sta) -{ - unsigned long flags; + done: + spin_lock_irqsave(&local->sta_lock, flags); - BUG_ON(!sta->sdata->bss); + if (indicate_tim) + __bss_tim_set(bss, sta->sta.aid); + else + __bss_tim_clear(bss, sta->sta.aid); + + if (local->ops->set_tim) { + local->tim_in_locked_section = true; + drv_set_tim(local, &sta->sta, indicate_tim); + local->tim_in_locked_section = false; + } - spin_lock_irqsave(&sta->local->sta_lock, flags); - __sta_info_clear_tim_bit(sta->sdata->bss, sta); - spin_unlock_irqrestore(&sta->local->sta_lock, flags); + spin_unlock_irqrestore(&local->sta_lock, flags); } static bool sta_info_buffer_expired(struct sta_info *sta, struct sk_buff *skb) @@ -711,21 +752,59 @@ static bool sta_info_buffer_expired(struct sta_info *sta, struct sk_buff *skb) } -static bool sta_info_cleanup_expire_buffered(struct ieee80211_local *local, - struct sta_info *sta) +static bool sta_info_cleanup_expire_buffered_ac(struct ieee80211_local *local, + struct sta_info *sta, int ac) { unsigned long flags; struct sk_buff *skb; + /* + * First check for frames that should expire on the filtered + * queue. Frames here were rejected by the driver and are on + * a separate queue to avoid reordering with normal PS-buffered + * frames. They also aren't accounted for right now in the + * total_ps_buffered counter. + */ + for (;;) { + spin_lock_irqsave(&sta->tx_filtered[ac].lock, flags); + skb = skb_peek(&sta->tx_filtered[ac]); + if (sta_info_buffer_expired(sta, skb)) + skb = __skb_dequeue(&sta->tx_filtered[ac]); + else + skb = NULL; + spin_unlock_irqrestore(&sta->tx_filtered[ac].lock, flags); + + /* + * Frames are queued in order, so if this one + * hasn't expired yet we can stop testing. If + * we actually reached the end of the queue we + * also need to stop, of course. + */ + if (!skb) + break; + dev_kfree_skb(skb); + } + + /* + * Now also check the normal PS-buffered queue, this will + * only find something if the filtered queue was emptied + * since the filtered frames are all before the normal PS + * buffered frames. + */ for (;;) { - spin_lock_irqsave(&sta->ps_tx_buf.lock, flags); - skb = skb_peek(&sta->ps_tx_buf); + spin_lock_irqsave(&sta->ps_tx_buf[ac].lock, flags); + skb = skb_peek(&sta->ps_tx_buf[ac]); if (sta_info_buffer_expired(sta, skb)) - skb = __skb_dequeue(&sta->ps_tx_buf); + skb = __skb_dequeue(&sta->ps_tx_buf[ac]); else skb = NULL; - spin_unlock_irqrestore(&sta->ps_tx_buf.lock, flags); + spin_unlock_irqrestore(&sta->ps_tx_buf[ac].lock, flags); + /* + * frames are queued in order, so if this one + * hasn't expired yet (or we reached the end of + * the queue) we can stop testing + */ if (!skb) break; @@ -735,22 +814,47 @@ static bool sta_info_cleanup_expire_buffered(struct ieee80211_local *local, sta->sta.addr); #endif dev_kfree_skb(skb); - - if (skb_queue_empty(&sta->ps_tx_buf) && - !test_sta_flags(sta, WLAN_STA_PS_DRIVER_BUF)) - sta_info_clear_tim_bit(sta); } - return !skb_queue_empty(&sta->ps_tx_buf); + /* + * Finally, recalculate the TIM bit for this station -- it might + * now be clear because the station was too slow to retrieve its + * frames. + */ + sta_info_recalc_tim(sta); + + /* + * Return whether there are any frames still buffered, this is + * used to check whether the cleanup timer still needs to run, + * if there are no frames we don't need to rearm the timer. + */ + return !(skb_queue_empty(&sta->ps_tx_buf[ac]) && + skb_queue_empty(&sta->tx_filtered[ac])); +} + +static bool sta_info_cleanup_expire_buffered(struct ieee80211_local *local, + struct sta_info *sta) +{ + bool have_buffered = false; + int ac; + + /* This is only necessary for stations on BSS interfaces */ + if (!sta->sdata->bss) + return false; + + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) + have_buffered |= + sta_info_cleanup_expire_buffered_ac(local, sta, ac); + + return have_buffered; } static int __must_check __sta_info_destroy(struct sta_info *sta) { struct ieee80211_local *local; struct ieee80211_sub_if_data *sdata; - struct sk_buff *skb; unsigned long flags; - int ret, i; + int ret, i, ac; might_sleep(); @@ -766,7 +870,7 @@ static int __must_check __sta_info_destroy(struct sta_info *sta) * sessions -- block that to make sure the tear-down * will be sufficient. */ - set_sta_flags(sta, WLAN_STA_BLOCK_BA); + set_sta_flag(sta, WLAN_STA_BLOCK_BA); ieee80211_sta_tear_down_BA_sessions(sta, true); spin_lock_irqsave(&local->sta_lock, flags); @@ -787,12 +891,15 @@ static int __must_check __sta_info_destroy(struct sta_info *sta) sta->dead = true; - if (test_and_clear_sta_flags(sta, - WLAN_STA_PS_STA | WLAN_STA_PS_DRIVER)) { + if (test_sta_flag(sta, WLAN_STA_PS_STA) || + test_sta_flag(sta, WLAN_STA_PS_DRIVER)) { BUG_ON(!sdata->bss); + clear_sta_flag(sta, WLAN_STA_PS_STA); + clear_sta_flag(sta, WLAN_STA_PS_DRIVER); + atomic_dec(&sdata->bss->num_sta_ps); - sta_info_clear_tim_bit(sta); + sta_info_recalc_tim(sta); } local->num_sta--; @@ -818,6 +925,12 @@ static int __must_check __sta_info_destroy(struct sta_info *sta) */ synchronize_rcu(); + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { + local->total_ps_buffered -= skb_queue_len(&sta->ps_tx_buf[ac]); + __skb_queue_purge(&sta->ps_tx_buf[ac]); + __skb_queue_purge(&sta->tx_filtered[ac]); + } + #ifdef CONFIG_MAC80211_MESH if (ieee80211_vif_is_mesh(&sdata->vif)) mesh_accept_plinks_update(sdata); @@ -840,14 +953,6 @@ static int __must_check __sta_info_destroy(struct sta_info *sta) } #endif - while ((skb = skb_dequeue(&sta->ps_tx_buf)) != NULL) { - local->total_ps_buffered--; - dev_kfree_skb_any(skb); - } - - while ((skb = skb_dequeue(&sta->tx_filtered)) != NULL) - dev_kfree_skb_any(skb); - __sta_info_free(local, sta); return 0; @@ -1013,7 +1118,8 @@ static void clear_sta_ps_flags(void *_sta) { struct sta_info *sta = _sta; - clear_sta_flags(sta, WLAN_STA_PS_DRIVER | WLAN_STA_PS_STA); + clear_sta_flag(sta, WLAN_STA_PS_DRIVER); + clear_sta_flag(sta, WLAN_STA_PS_STA); } /* powersave support code */ @@ -1021,88 +1127,343 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta) { struct ieee80211_sub_if_data *sdata = sta->sdata; struct ieee80211_local *local = sdata->local; - int sent, buffered; + struct sk_buff_head pending; + int filtered = 0, buffered = 0, ac; + + clear_sta_flag(sta, WLAN_STA_SP); + + BUILD_BUG_ON(BITS_TO_LONGS(STA_TID_NUM) > 1); + sta->driver_buffered_tids = 0; - clear_sta_flags(sta, WLAN_STA_PS_DRIVER_BUF); if (!(local->hw.flags & IEEE80211_HW_AP_LINK_PS)) drv_sta_notify(local, sdata, STA_NOTIFY_AWAKE, &sta->sta); - if (!skb_queue_empty(&sta->ps_tx_buf)) - sta_info_clear_tim_bit(sta); + skb_queue_head_init(&pending); /* Send all buffered frames to the station */ - sent = ieee80211_add_pending_skbs(local, &sta->tx_filtered); - buffered = ieee80211_add_pending_skbs_fn(local, &sta->ps_tx_buf, - clear_sta_ps_flags, sta); - sent += buffered; + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { + int count = skb_queue_len(&pending), tmp; + + skb_queue_splice_tail_init(&sta->tx_filtered[ac], &pending); + tmp = skb_queue_len(&pending); + filtered += tmp - count; + count = tmp; + + skb_queue_splice_tail_init(&sta->ps_tx_buf[ac], &pending); + tmp = skb_queue_len(&pending); + buffered += tmp - count; + } + + ieee80211_add_pending_skbs_fn(local, &pending, clear_sta_ps_flags, sta); + local->total_ps_buffered -= buffered; + sta_info_recalc_tim(sta); + #ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG printk(KERN_DEBUG "%s: STA %pM aid %d sending %d filtered/%d PS frames " "since STA not sleeping anymore\n", sdata->name, - sta->sta.addr, sta->sta.aid, sent - buffered, buffered); + sta->sta.addr, sta->sta.aid, filtered, buffered); #endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */ } -void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta) +static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata, + struct sta_info *sta, int tid, + enum ieee80211_frame_release_type reason) { - struct ieee80211_sub_if_data *sdata = sta->sdata; struct ieee80211_local *local = sdata->local; + struct ieee80211_qos_hdr *nullfunc; struct sk_buff *skb; - int no_pending_pkts; + int size = sizeof(*nullfunc); + __le16 fc; + bool qos = test_sta_flag(sta, WLAN_STA_WME); + struct ieee80211_tx_info *info; - skb = skb_dequeue(&sta->tx_filtered); - if (!skb) { - skb = skb_dequeue(&sta->ps_tx_buf); - if (skb) - local->total_ps_buffered--; + if (qos) { + fc = cpu_to_le16(IEEE80211_FTYPE_DATA | + IEEE80211_STYPE_QOS_NULLFUNC | + IEEE80211_FCTL_FROMDS); + } else { + size -= 2; + fc = cpu_to_le16(IEEE80211_FTYPE_DATA | + IEEE80211_STYPE_NULLFUNC | + IEEE80211_FCTL_FROMDS); } - no_pending_pkts = skb_queue_empty(&sta->tx_filtered) && - skb_queue_empty(&sta->ps_tx_buf); - if (skb) { - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - struct ieee80211_hdr *hdr = - (struct ieee80211_hdr *) skb->data; + skb = dev_alloc_skb(local->hw.extra_tx_headroom + size); + if (!skb) + return; + + skb_reserve(skb, local->hw.extra_tx_headroom); + + nullfunc = (void *) skb_put(skb, size); + nullfunc->frame_control = fc; + nullfunc->duration_id = 0; + memcpy(nullfunc->addr1, sta->sta.addr, ETH_ALEN); + memcpy(nullfunc->addr2, sdata->vif.addr, ETH_ALEN); + memcpy(nullfunc->addr3, sdata->vif.addr, ETH_ALEN); + + if (qos) { + skb->priority = tid; + + skb_set_queue_mapping(skb, ieee802_1d_to_ac[tid]); + + nullfunc->qos_ctrl = cpu_to_le16(tid); + + if (reason == IEEE80211_FRAME_RELEASE_UAPSD) + nullfunc->qos_ctrl |= + cpu_to_le16(IEEE80211_QOS_CTL_EOSP); + } + + info = IEEE80211_SKB_CB(skb); + + /* + * Tell TX path to send this frame even though the + * STA may still remain is PS mode after this frame + * exchange. Also set EOSP to indicate this packet + * ends the poll/service period. + */ + info->flags |= IEEE80211_TX_CTL_POLL_RESPONSE | + IEEE80211_TX_STATUS_EOSP | + IEEE80211_TX_CTL_REQ_TX_STATUS; + + drv_allow_buffered_frames(local, sta, BIT(tid), 1, reason, false); + + ieee80211_xmit(sdata, skb); +} + +static void +ieee80211_sta_ps_deliver_response(struct sta_info *sta, + int n_frames, u8 ignored_acs, + enum ieee80211_frame_release_type reason) +{ + struct ieee80211_sub_if_data *sdata = sta->sdata; + struct ieee80211_local *local = sdata->local; + bool found = false; + bool more_data = false; + int ac; + unsigned long driver_release_tids = 0; + struct sk_buff_head frames; + + /* Service or PS-Poll period starts */ + set_sta_flag(sta, WLAN_STA_SP); + + __skb_queue_head_init(&frames); + + /* + * Get response frame(s) and more data bit for it. + */ + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { + unsigned long tids; + + if (ignored_acs & BIT(ac)) + continue; + + tids = ieee80211_tids_for_ac(ac); + + if (!found) { + driver_release_tids = sta->driver_buffered_tids & tids; + if (driver_release_tids) { + found = true; + } else { + struct sk_buff *skb; + + while (n_frames > 0) { + skb = skb_dequeue(&sta->tx_filtered[ac]); + if (!skb) { + skb = skb_dequeue( + &sta->ps_tx_buf[ac]); + if (skb) + local->total_ps_buffered--; + } + if (!skb) + break; + n_frames--; + found = true; + __skb_queue_tail(&frames, skb); + } + } + + /* + * If the driver has data on more than one TID then + * certainly there's more data if we release just a + * single frame now (from a single TID). + */ + if (reason == IEEE80211_FRAME_RELEASE_PSPOLL && + hweight16(driver_release_tids) > 1) { + more_data = true; + driver_release_tids = + BIT(ffs(driver_release_tids) - 1); + break; + } + } + + if (!skb_queue_empty(&sta->tx_filtered[ac]) || + !skb_queue_empty(&sta->ps_tx_buf[ac])) { + more_data = true; + break; + } + } + + if (!found) { + int tid; /* - * Tell TX path to send this frame even though the STA may - * still remain is PS mode after this frame exchange. + * For PS-Poll, this can only happen due to a race condition + * when we set the TIM bit and the station notices it, but + * before it can poll for the frame we expire it. + * + * For uAPSD, this is said in the standard (11.2.1.5 h): + * At each unscheduled SP for a non-AP STA, the AP shall + * attempt to transmit at least one MSDU or MMPDU, but no + * more than the value specified in the Max SP Length field + * in the QoS Capability element from delivery-enabled ACs, + * that are destined for the non-AP STA. + * + * Since we have no other MSDU/MMPDU, transmit a QoS null frame. */ - info->flags |= IEEE80211_TX_CTL_PSPOLL_RESPONSE; -#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG - printk(KERN_DEBUG "STA %pM aid %d: PS Poll (entries after %d)\n", - sta->sta.addr, sta->sta.aid, - skb_queue_len(&sta->ps_tx_buf)); -#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */ + /* This will evaluate to 1, 3, 5 or 7. */ + tid = 7 - ((ffs(~ignored_acs) - 1) << 1); - /* Use MoreData flag to indicate whether there are more - * buffered frames for this STA */ - if (no_pending_pkts) - hdr->frame_control &= cpu_to_le16(~IEEE80211_FCTL_MOREDATA); - else - hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREDATA); + ieee80211_send_null_response(sdata, sta, tid, reason); + return; + } - ieee80211_add_pending_skb(local, skb); + if (!driver_release_tids) { + struct sk_buff_head pending; + struct sk_buff *skb; + int num = 0; + u16 tids = 0; + + skb_queue_head_init(&pending); + + while ((skb = __skb_dequeue(&frames))) { + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_hdr *hdr = (void *) skb->data; + u8 *qoshdr = NULL; + + num++; + + /* + * Tell TX path to send this frame even though the + * STA may still remain is PS mode after this frame + * exchange. + */ + info->flags |= IEEE80211_TX_CTL_POLL_RESPONSE; + + /* + * Use MoreData flag to indicate whether there are + * more buffered frames for this STA + */ + if (!more_data) + hdr->frame_control &= + cpu_to_le16(~IEEE80211_FCTL_MOREDATA); + else + hdr->frame_control |= + cpu_to_le16(IEEE80211_FCTL_MOREDATA); + + if (ieee80211_is_data_qos(hdr->frame_control) || + ieee80211_is_qos_nullfunc(hdr->frame_control)) + qoshdr = ieee80211_get_qos_ctl(hdr); + + /* set EOSP for the frame */ + if (reason == IEEE80211_FRAME_RELEASE_UAPSD && + qoshdr && skb_queue_empty(&frames)) + *qoshdr |= IEEE80211_QOS_CTL_EOSP; + + info->flags |= IEEE80211_TX_STATUS_EOSP | + IEEE80211_TX_CTL_REQ_TX_STATUS; + + if (qoshdr) + tids |= BIT(*qoshdr & IEEE80211_QOS_CTL_TID_MASK); + else + tids |= BIT(0); + + __skb_queue_tail(&pending, skb); + } - if (no_pending_pkts) - sta_info_clear_tim_bit(sta); -#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG + drv_allow_buffered_frames(local, sta, tids, num, + reason, more_data); + + ieee80211_add_pending_skbs(local, &pending); + + sta_info_recalc_tim(sta); } else { /* - * FIXME: This can be the result of a race condition between - * us expiring a frame and the station polling for it. - * Should we send it a null-func frame indicating we - * have nothing buffered for it? + * We need to release a frame that is buffered somewhere in the + * driver ... it'll have to handle that. + * Note that, as per the comment above, it'll also have to see + * if there is more than just one frame on the specific TID that + * we're releasing from, and it needs to set the more-data bit + * accordingly if we tell it that there's no more data. If we do + * tell it there's more data, then of course the more-data bit + * needs to be set anyway. + */ + drv_release_buffered_frames(local, sta, driver_release_tids, + n_frames, reason, more_data); + + /* + * Note that we don't recalculate the TIM bit here as it would + * most likely have no effect at all unless the driver told us + * that the TID became empty before returning here from the + * release function. + * Either way, however, when the driver tells us that the TID + * became empty we'll do the TIM recalculation. */ - printk(KERN_DEBUG "%s: STA %pM sent PS Poll even " - "though there are no buffered frames for it\n", - sdata->name, sta->sta.addr); -#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */ } } +void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta) +{ + u8 ignore_for_response = sta->sta.uapsd_queues; + + /* + * If all ACs are delivery-enabled then we should reply + * from any of them, if only some are enabled we reply + * only from the non-enabled ones. + */ + if (ignore_for_response == BIT(IEEE80211_NUM_ACS) - 1) + ignore_for_response = 0; + + ieee80211_sta_ps_deliver_response(sta, 1, ignore_for_response, + IEEE80211_FRAME_RELEASE_PSPOLL); +} + +void ieee80211_sta_ps_deliver_uapsd(struct sta_info *sta) +{ + int n_frames = sta->sta.max_sp; + u8 delivery_enabled = sta->sta.uapsd_queues; + + /* + * If we ever grow support for TSPEC this might happen if + * the TSPEC update from hostapd comes in between a trigger + * frame setting WLAN_STA_UAPSD in the RX path and this + * actually getting called. + */ + if (!delivery_enabled) + return; + + switch (sta->sta.max_sp) { + case 1: + n_frames = 2; + break; + case 2: + n_frames = 4; + break; + case 3: + n_frames = 6; + break; + case 0: + /* XXX: what is a good value? */ + n_frames = 8; + break; + } + + ieee80211_sta_ps_deliver_response(sta, n_frames, ~delivery_enabled, + IEEE80211_FRAME_RELEASE_UAPSD); +} + void ieee80211_sta_block_awake(struct ieee80211_hw *hw, struct ieee80211_sta *pubsta, bool block) { @@ -1111,17 +1472,50 @@ void ieee80211_sta_block_awake(struct ieee80211_hw *hw, trace_api_sta_block_awake(sta->local, pubsta, block); if (block) - set_sta_flags(sta, WLAN_STA_PS_DRIVER); - else if (test_sta_flags(sta, WLAN_STA_PS_DRIVER)) + set_sta_flag(sta, WLAN_STA_PS_DRIVER); + else if (test_sta_flag(sta, WLAN_STA_PS_DRIVER)) ieee80211_queue_work(hw, &sta->drv_unblock_wk); } EXPORT_SYMBOL(ieee80211_sta_block_awake); -void ieee80211_sta_set_tim(struct ieee80211_sta *pubsta) +void ieee80211_sta_eosp_irqsafe(struct ieee80211_sta *pubsta) +{ + struct sta_info *sta = container_of(pubsta, struct sta_info, sta); + struct ieee80211_local *local = sta->local; + struct sk_buff *skb; + struct skb_eosp_msg_data *data; + + trace_api_eosp(local, pubsta); + + skb = alloc_skb(0, GFP_ATOMIC); + if (!skb) { + /* too bad ... but race is better than loss */ + clear_sta_flag(sta, WLAN_STA_SP); + return; + } + + data = (void *)skb->cb; + memcpy(data->sta, pubsta->addr, ETH_ALEN); + memcpy(data->iface, sta->sdata->vif.addr, ETH_ALEN); + skb->pkt_type = IEEE80211_EOSP_MSG; + skb_queue_tail(&local->skb_queue, skb); + tasklet_schedule(&local->tasklet); +} +EXPORT_SYMBOL(ieee80211_sta_eosp_irqsafe); + +void ieee80211_sta_set_buffered(struct ieee80211_sta *pubsta, + u8 tid, bool buffered) { struct sta_info *sta = container_of(pubsta, struct sta_info, sta); - set_sta_flags(sta, WLAN_STA_PS_DRIVER_BUF); - sta_info_set_tim_bit(sta); + if (WARN_ON(tid >= STA_TID_NUM)) + return; + + if (buffered) + set_bit(tid, &sta->driver_buffered_tids); + else + clear_bit(tid, &sta->driver_buffered_tids); + + sta_info_recalc_tim(sta); } -EXPORT_SYMBOL(ieee80211_sta_set_tim); +EXPORT_SYMBOL(ieee80211_sta_set_buffered); |