Commit 40b96408 authored by Johannes Berg's avatar Johannes Berg Committed by John W. Linville

mac80211: explicitly notify drivers of frame release

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: default avatarJohannes Berg <johannes.berg@intel.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 5bade101
...@@ -1973,6 +1973,18 @@ enum ieee80211_frame_release_type { ...@@ -1973,6 +1973,18 @@ enum ieee80211_frame_release_type {
* service period ends, the driver must set %IEEE80211_TX_STATUS_EOSP * 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.
* This callback must be atomic. * 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 { struct ieee80211_ops {
void (*tx)(struct ieee80211_hw *hw, struct sk_buff *skb); void (*tx)(struct ieee80211_hw *hw, struct sk_buff *skb);
...@@ -2088,6 +2100,11 @@ struct ieee80211_ops { ...@@ -2088,6 +2100,11 @@ struct ieee80211_ops {
void (*rssi_callback)(struct ieee80211_hw *hw, void (*rssi_callback)(struct ieee80211_hw *hw,
enum ieee80211_rssi_event rssi_event); 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, void (*release_buffered_frames)(struct ieee80211_hw *hw,
struct ieee80211_sta *sta, struct ieee80211_sta *sta,
u16 tids, int num_frames, u16 tids, int num_frames,
......
...@@ -685,4 +685,19 @@ drv_release_buffered_frames(struct ieee80211_local *local, ...@@ -685,4 +685,19 @@ drv_release_buffered_frames(struct ieee80211_local *local,
more_data); more_data);
trace_drv_return_void(local); 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 */ #endif /* __MAC80211_DRIVER_OPS */
...@@ -1129,7 +1129,7 @@ TRACE_EVENT(drv_rssi_callback, ...@@ -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, TP_PROTO(struct ieee80211_local *local,
struct ieee80211_sta *sta, struct ieee80211_sta *sta,
u16 tids, int num_frames, u16 tids, int num_frames,
...@@ -1164,6 +1164,26 @@ TRACE_EVENT(drv_release_buffered_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. * Tracing for API calls that drivers call.
*/ */
......
...@@ -1169,7 +1169,7 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta) ...@@ -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, static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata,
struct sta_info *sta, int tid, struct sta_info *sta, int tid,
bool uapsd) enum ieee80211_frame_release_type reason)
{ {
struct ieee80211_local *local = sdata->local; struct ieee80211_local *local = sdata->local;
struct ieee80211_qos_hdr *nullfunc; struct ieee80211_qos_hdr *nullfunc;
...@@ -1210,7 +1210,7 @@ static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata, ...@@ -1210,7 +1210,7 @@ static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata,
nullfunc->qos_ctrl = cpu_to_le16(tid); nullfunc->qos_ctrl = cpu_to_le16(tid);
if (uapsd) if (reason == IEEE80211_FRAME_RELEASE_UAPSD)
nullfunc->qos_ctrl |= nullfunc->qos_ctrl |=
cpu_to_le16(IEEE80211_QOS_CTL_EOSP); cpu_to_le16(IEEE80211_QOS_CTL_EOSP);
} }
...@@ -1227,6 +1227,8 @@ static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata, ...@@ -1227,6 +1227,8 @@ static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata,
IEEE80211_TX_STATUS_EOSP | IEEE80211_TX_STATUS_EOSP |
IEEE80211_TX_CTL_REQ_TX_STATUS; IEEE80211_TX_CTL_REQ_TX_STATUS;
drv_allow_buffered_frames(local, sta, BIT(tid), 1, reason, false);
ieee80211_xmit(sdata, skb); ieee80211_xmit(sdata, skb);
} }
...@@ -1324,20 +1326,24 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta, ...@@ -1324,20 +1326,24 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta,
/* This will evaluate to 1, 3, 5 or 7. */ /* This will evaluate to 1, 3, 5 or 7. */
tid = 7 - ((ffs(~ignored_acs) - 1) << 1); tid = 7 - ((ffs(~ignored_acs) - 1) << 1);
ieee80211_send_null_response(sdata, sta, tid, ieee80211_send_null_response(sdata, sta, tid, reason);
reason == IEEE80211_FRAME_RELEASE_UAPSD);
return; return;
} }
if (!driver_release_tids) { if (!driver_release_tids) {
struct sk_buff_head pending; struct sk_buff_head pending;
struct sk_buff *skb; struct sk_buff *skb;
int num = 0;
u16 tids = 0;
skb_queue_head_init(&pending); skb_queue_head_init(&pending);
while ((skb = __skb_dequeue(&frames))) { while ((skb = __skb_dequeue(&frames))) {
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct ieee80211_hdr *hdr = (void *) skb->data; struct ieee80211_hdr *hdr = (void *) skb->data;
u8 *qoshdr = NULL;
num++;
/* /*
* Tell TX path to send this frame even though the * Tell TX path to send this frame even though the
...@@ -1357,19 +1363,29 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta, ...@@ -1357,19 +1363,29 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta,
hdr->frame_control |= hdr->frame_control |=
cpu_to_le16(IEEE80211_FCTL_MOREDATA); cpu_to_le16(IEEE80211_FCTL_MOREDATA);
if (reason == IEEE80211_FRAME_RELEASE_UAPSD && if (ieee80211_is_data_qos(hdr->frame_control) ||
skb_queue_empty(&frames)) { ieee80211_is_qos_nullfunc(hdr->frame_control))
qoshdr = ieee80211_get_qos_ctl(hdr);
/* set EOSP for the frame */ /* set EOSP for the frame */
u8 *p = ieee80211_get_qos_ctl(hdr); if (reason == IEEE80211_FRAME_RELEASE_UAPSD &&
*p |= IEEE80211_QOS_CTL_EOSP; qoshdr && skb_queue_empty(&frames))
} *qoshdr |= IEEE80211_QOS_CTL_EOSP;
info->flags |= IEEE80211_TX_STATUS_EOSP | info->flags |= IEEE80211_TX_STATUS_EOSP |
IEEE80211_TX_CTL_REQ_TX_STATUS; 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); __skb_queue_tail(&pending, skb);
} }
drv_allow_buffered_frames(local, sta, tids, num,
reason, more_data);
ieee80211_add_pending_skbs(local, &pending); ieee80211_add_pending_skbs(local, &pending);
sta_info_recalc_tim(sta); sta_info_recalc_tim(sta);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment