mac80211: explicitly notify drivers of frame release
authorJohannes Berg <johannes.berg@intel.com>
Thu, 29 Sep 2011 14:04:38 +0000 (16:04 +0200)
committerJohn W. Linville <linville@tuxdriver.com>
Fri, 30 Sep 2011 19:57:21 +0000 (15:57 -0400)
iwlwifi needs to know the number of frames that are
going to be sent to a station while it is asleep so
it can properly handle the uCode blocking of that
station.

Before uAPSD, we got by by telling the device that
a single frame was going to be released whenever we
encountered IEEE80211_TX_CTL_POLL_RESPONSE. With
uAPSD, however, that is no longer possible since
there could be more than a single frame.

To support this model, add a new callback to notify
drivers when frames are going to be released.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
include/net/mac80211.h
net/mac80211/driver-ops.h
net/mac80211/driver-trace.h
net/mac80211/sta_info.c

index ee6449eff7dcb96e82301d14458a3c89cfac3588..778572d38bd36a14d83e0b5d959cd18eb030ccc9 100644 (file)
@@ -1973,6 +1973,18 @@ enum ieee80211_frame_release_type {
  *     service period ends, the driver must set %IEEE80211_TX_STATUS_EOSP
  *     on the last frame in the SP.
  *     This callback must be atomic.
+ * @allow_buffered_frames: Prepare device to allow the given number of frames
+ *     to go out to the given station. The frames will be sent by mac80211
+ *     via the usual TX path after this call. The TX information for frames
+ *     released will also have the %IEEE80211_TX_CTL_POLL_RESPONSE flag set
+ *     and the last one will also have %IEEE80211_TX_STATUS_EOSP set. In case
+ *     frames from multiple TIDs are released and the driver might reorder
+ *     them between the TIDs, it must set the %IEEE80211_TX_STATUS_EOSP flag
+ *     on the last frame and clear it on all others and also handle the EOSP
+ *     bit in the QoS header correctly.
+ *     The @tids parameter is a bitmap and tells the driver which TIDs the
+ *     frames will be on; it will at most have two bits set.
+ *     This callback must be atomic.
  */
 struct ieee80211_ops {
        void (*tx)(struct ieee80211_hw *hw, struct sk_buff *skb);
@@ -2088,6 +2100,11 @@ struct ieee80211_ops {
        void (*rssi_callback)(struct ieee80211_hw *hw,
                              enum ieee80211_rssi_event rssi_event);
 
+       void (*allow_buffered_frames)(struct ieee80211_hw *hw,
+                                     struct ieee80211_sta *sta,
+                                     u16 tids, int num_frames,
+                                     enum ieee80211_frame_release_type reason,
+                                     bool more_data);
        void (*release_buffered_frames)(struct ieee80211_hw *hw,
                                        struct ieee80211_sta *sta,
                                        u16 tids, int num_frames,
index 8fa0d2edf54c8d1d9b4f716efe2d26b6a2ae3850..68721d379fe1adc1173268ae025891d99ef2546b 100644 (file)
@@ -685,4 +685,19 @@ drv_release_buffered_frames(struct ieee80211_local *local,
                                                    more_data);
        trace_drv_return_void(local);
 }
+
+static inline void
+drv_allow_buffered_frames(struct ieee80211_local *local,
+                         struct sta_info *sta, u16 tids, int num_frames,
+                         enum ieee80211_frame_release_type reason,
+                         bool more_data)
+{
+       trace_drv_allow_buffered_frames(local, &sta->sta, tids, num_frames,
+                                       reason, more_data);
+       if (local->ops->allow_buffered_frames)
+               local->ops->allow_buffered_frames(&local->hw, &sta->sta,
+                                                 tids, num_frames, reason,
+                                                 more_data);
+       trace_drv_return_void(local);
+}
 #endif /* __MAC80211_DRIVER_OPS */
index 531fbd086794989de897e0aa1cb13934cfaa61cb..aef08969e353fc365c0239f7fbe2b53fb6cd2b3a 100644 (file)
@@ -1129,7 +1129,7 @@ TRACE_EVENT(drv_rssi_callback,
        )
 );
 
-TRACE_EVENT(drv_release_buffered_frames,
+DECLARE_EVENT_CLASS(release_evt,
        TP_PROTO(struct ieee80211_local *local,
                 struct ieee80211_sta *sta,
                 u16 tids, int num_frames,
@@ -1164,6 +1164,26 @@ TRACE_EVENT(drv_release_buffered_frames,
        )
 );
 
+DEFINE_EVENT(release_evt, drv_release_buffered_frames,
+       TP_PROTO(struct ieee80211_local *local,
+                struct ieee80211_sta *sta,
+                u16 tids, int num_frames,
+                enum ieee80211_frame_release_type reason,
+                bool more_data),
+
+       TP_ARGS(local, sta, tids, num_frames, reason, more_data)
+);
+
+DEFINE_EVENT(release_evt, drv_allow_buffered_frames,
+       TP_PROTO(struct ieee80211_local *local,
+                struct ieee80211_sta *sta,
+                u16 tids, int num_frames,
+                enum ieee80211_frame_release_type reason,
+                bool more_data),
+
+       TP_ARGS(local, sta, tids, num_frames, reason, more_data)
+);
+
 /*
  * Tracing for API calls that drivers call.
  */
index a00358224cd5de7645ed15fa59c7c676fff08fa7..907b42081f3c6e98d0ad2b12f0b6a19302c6d247 100644 (file)
@@ -1169,7 +1169,7 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta)
 
 static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata,
                                         struct sta_info *sta, int tid,
-                                        bool uapsd)
+                                        enum ieee80211_frame_release_type reason)
 {
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_qos_hdr *nullfunc;
@@ -1210,7 +1210,7 @@ static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata,
 
                nullfunc->qos_ctrl = cpu_to_le16(tid);
 
-               if (uapsd)
+               if (reason == IEEE80211_FRAME_RELEASE_UAPSD)
                        nullfunc->qos_ctrl |=
                                cpu_to_le16(IEEE80211_QOS_CTL_EOSP);
        }
@@ -1227,6 +1227,8 @@ static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata,
                       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);
 }
 
@@ -1324,20 +1326,24 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta,
                /* This will evaluate to 1, 3, 5 or 7. */
                tid = 7 - ((ffs(~ignored_acs) - 1) << 1);
 
-               ieee80211_send_null_response(sdata, sta, tid,
-                               reason == IEEE80211_FRAME_RELEASE_UAPSD);
+               ieee80211_send_null_response(sdata, sta, tid, reason);
                return;
        }
 
        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
@@ -1357,19 +1363,29 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta,
                                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 &&
-                           skb_queue_empty(&frames)) {
-                               /* set EOSP for the frame */
-                               u8 *p = ieee80211_get_qos_ctl(hdr);
-                               *p |= IEEE80211_QOS_CTL_EOSP;
-                       }
+                           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);
                }
 
+               drv_allow_buffered_frames(local, sta, tids, num,
+                                         reason, more_data);
+
                ieee80211_add_pending_skbs(local, &pending);
 
                sta_info_recalc_tim(sta);