From: Johannes Berg <johannes.berg@xxxxxxxxx> iwlwifi has a separate EOSP notification from the device, and to make use of that properly it needs to be passed to mac80211. To be able to mix with tx_status_irqsafe and rx_irqsafe it also needs to be an "_irqsafe" version in the sense that it goes through the tasklet, the actual flag clearing would be IRQ-safe but doing it directly would cause reordering issues. Signed-off-by: Johannes Berg <johannes.berg@xxxxxxxxx> --- include/net/mac80211.h | 19 ++++++++++++++++++- net/mac80211/ieee80211_i.h | 5 +++++ net/mac80211/main.c | 13 +++++++++++++ net/mac80211/sta_info.c | 23 +++++++++++++++++++++++ 4 files changed, 59 insertions(+), 1 deletion(-) --- a/net/mac80211/sta_info.c 2011-09-06 22:25:59.000000000 +0200 +++ b/net/mac80211/sta_info.c 2011-09-06 22:26:00.000000000 +0200 @@ -1356,6 +1356,29 @@ void ieee80211_sta_block_awake(struct ie } EXPORT_SYMBOL(ieee80211_sta_block_awake); +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; + + skb = alloc_skb(0, GFP_ATOMIC); + if (!skb) { + /* too bad ... but race is better than loss */ + clear_sta_flags(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) { --- a/include/net/mac80211.h 2011-09-06 22:25:59.000000000 +0200 +++ b/include/net/mac80211.h 2011-09-06 22:26:00.000000000 +0200 @@ -1948,7 +1948,8 @@ enum ieee80211_frame_release_type { * at least one, however). In this case it is also responsible for * setting the EOSP flag in the QoS header of the frames. Also, when the * service period ends, the driver must set %IEEE80211_TX_STATUS_EOSP - * on the last frame in the SP. + * on the last frame in the SP. Alternatively, it may call the function + * ieee80211_sta_eosp_irqsafe() to inform mac80211 of the end of the SP. * This callback must be atomic. */ struct ieee80211_ops { @@ -3072,6 +3073,22 @@ void ieee80211_sta_block_awake(struct ie struct ieee80211_sta *pubsta, bool block); /** + * ieee80211_sta_eosp - notify mac80211 about end of SP + * @pubsta: the station + * + * When a device transmits frames in a way that it can't + * tell us in the TX status about the EOSP, it must call + * this function instead. + * + * Note that there is no non-_irqsafe version right now as + * it wasn't needed, but just like _tx_status() and _rx() + * must not be mixed in irqsafe/non-irqsafe versions, this + * function must not be mixed with those either. Use the + * all irqsafe, or all non-irqsafe, don't mix! + */ +void ieee80211_sta_eosp_irqsafe(struct ieee80211_sta *pubsta); + +/** * ieee80211_iter_keys - iterate keys programmed into the device * @hw: pointer obtained from ieee80211_alloc_hw() * @vif: virtual interface to iterate, may be %NULL for all --- a/net/mac80211/main.c 2011-09-06 22:25:32.000000000 +0200 +++ b/net/mac80211/main.c 2011-09-06 22:26:00.000000000 +0200 @@ -325,6 +325,8 @@ u32 ieee80211_reset_erp_info(struct ieee static void ieee80211_tasklet_handler(unsigned long data) { struct ieee80211_local *local = (struct ieee80211_local *) data; + struct sta_info *sta, *tmp; + struct skb_eosp_msg_data *eosp_data; struct sk_buff *skb; while ((skb = skb_dequeue(&local->skb_queue)) || @@ -340,6 +342,17 @@ static void ieee80211_tasklet_handler(un skb->pkt_type = 0; ieee80211_tx_status(local_to_hw(local), skb); break; + case IEEE80211_EOSP_MSG: + eosp_data = (void *)skb->cb; + for_each_sta_info(local, eosp_data->sta, sta, tmp) { + /* skip wrong virtual interface */ + if (memcmp(eosp_data->iface, + sta->sdata->vif.addr, ETH_ALEN)) + continue; + clear_sta_flags(sta, WLAN_STA_SP); + break; + } + break; default: WARN(1, "mac80211: Packet is of unknown type %d\n", skb->pkt_type); --- a/net/mac80211/ieee80211_i.h 2011-09-06 22:25:32.000000000 +0200 +++ b/net/mac80211/ieee80211_i.h 2011-09-06 22:26:00.000000000 +0200 @@ -661,6 +661,11 @@ enum sdata_queue_type { enum { IEEE80211_RX_MSG = 1, IEEE80211_TX_STATUS_MSG = 2, + IEEE80211_EOSP_MSG = 3, +}; + +struct skb_eosp_msg_data { + u8 sta[ETH_ALEN], iface[ETH_ALEN]; }; enum queue_stop_reason { -- To unsubscribe from this list: send the line "unsubscribe linux-wireless" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html