From 84084a3197a9fdec10fa542c0df11928a784e7fc Mon Sep 17 00:00:00 2001 From: Mat Martineau Date: Fri, 22 Jul 2011 14:54:00 -0700 Subject: Bluetooth: Perform L2CAP SDU reassembly without copying data Use sk_buff fragment capabilities to link together incoming skbs instead of allocating a new skb for reassembly and copying. The new reassembly code works equally well for ERTM and streaming mode, so there is now one reassembly function instead of two. Signed-off-by: Mat Martineau Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/l2cap.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 7f878b9d5642..ab90ae0970a6 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -354,8 +354,8 @@ struct l2cap_chan { __u8 retry_count; __u8 num_acked; __u16 sdu_len; - __u16 partial_sdu_len; struct sk_buff *sdu; + struct sk_buff *sdu_last_frag; __u8 remote_tx_win; __u8 remote_max_tx; @@ -448,7 +448,6 @@ enum { #define L2CAP_CONF_MAX_CONF_RSP 2 enum { - CONN_SAR_SDU, CONN_SREJ_SENT, CONN_WAIT_F, CONN_SREJ_ACT, -- cgit v1.2.3 From 3b9ce80ce96aeaeacab5e26442987df45584a049 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 27 Sep 2011 20:56:12 +0200 Subject: cfg80211/mac80211: apply station uAPSD parameters selectively Currently, when hostapd sets the station as authorized we also overwrite its uAPSD parameter. This obviously leads to buggy behaviour (later, with my patches that actually add uAPSD support). To fix this, only apply those parameters if they were actually set in nl80211, and to achieve that add a bitmap of things to apply. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- include/net/cfg80211.h | 12 ++++++++++++ net/mac80211/cfg.c | 6 ++++-- net/wireless/nl80211.c | 2 ++ 3 files changed, 18 insertions(+), 2 deletions(-) (limited to 'include/net') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 31d823a3092b..34b8f269976b 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -423,6 +423,17 @@ enum plink_actions { PLINK_ACTION_BLOCK, }; +/** + * enum station_parameters_apply_mask - station parameter values to apply + * @STATION_PARAM_APPLY_UAPSD: apply new uAPSD parameters (uapsd_queues, max_sp) + * + * Not all station parameters have in-band "no change" signalling, + * for those that don't these flags will are used. + */ +enum station_parameters_apply_mask { + STATION_PARAM_APPLY_UAPSD = BIT(0), +}; + /** * struct station_parameters - station parameters * @@ -450,6 +461,7 @@ struct station_parameters { u8 *supported_rates; struct net_device *vlan; u32 sta_flags_mask, sta_flags_set; + u32 sta_modify_mask; int listen_interval; u16 aid; u8 supported_rates_len; diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 8fef3cddbc4f..13061ebc93ef 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -714,8 +714,10 @@ static void sta_apply_parameters(struct ieee80211_local *local, } spin_unlock_irqrestore(&sta->flaglock, flags); - sta->sta.uapsd_queues = params->uapsd_queues; - sta->sta.max_sp = params->max_sp; + if (params->sta_modify_mask & STATION_PARAM_APPLY_UAPSD) { + sta->sta.uapsd_queues = params->uapsd_queues; + sta->sta.max_sp = params->max_sp; + } /* * cfg80211 validates this (1-2007) and allows setting the AID diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index b85075761e24..3799623e7f46 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -2643,6 +2643,8 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) if (params.max_sp & ~IEEE80211_WMM_IE_STA_QOSINFO_SP_MASK) return -EINVAL; + + params.sta_modify_mask |= STATION_PARAM_APPLY_UAPSD; } if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && -- cgit v1.2.3 From 109086ce0b0f94760bdb0e8e2566ff8a2d673639 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Wed, 28 Sep 2011 14:12:50 +0300 Subject: nl80211: support sending TDLS commands/frames Add support for sending high-level TDLS commands and TDLS frames via NL80211_CMD_TDLS_OPER and NL80211_CMD_TDLS_MGMT, respectively. Add appropriate cfg80211 callbacks for lower level drivers. Add wiphy capability flags for TDLS support and advertise them via nl80211. Signed-off-by: Arik Nemtsov Cc: Kalyan C Gaddam Signed-off-by: John W. Linville --- include/linux/nl80211.h | 42 +++++++++++++++++++++++++ include/net/cfg80211.h | 17 +++++++++++ net/wireless/nl80211.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 139 insertions(+), 1 deletion(-) (limited to 'include/net') diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index c73582fb9d20..a5ab23df5b17 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -506,6 +506,9 @@ * @NL80211_CMD_PMKSA_CANDIDATE: This is used as an event to inform userspace * of PMKSA caching dandidates. * + * @NL80211_CMD_TDLS_OPER: Perform a high-level TDLS command (e.g. link setup). + * @NL80211_CMD_TDLS_MGMT: Send a TDLS management frame. + * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -632,6 +635,9 @@ enum nl80211_commands { NL80211_CMD_PMKSA_CANDIDATE, + NL80211_CMD_TDLS_OPER, + NL80211_CMD_TDLS_MGMT, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ @@ -1089,6 +1095,20 @@ enum nl80211_commands { * This attribute is used with %NL80211_CMD_TRIGGER_SCAN and * %NL80211_CMD_FRAME commands. * + * @NL80211_ATTR_TDLS_ACTION: Low level TDLS action code (e.g. link setup + * request, link setup confirm, link teardown, etc.). Values are + * described in the TDLS (802.11z) specification. + * @NL80211_ATTR_TDLS_DIALOG_TOKEN: Non-zero token for uniquely identifying a + * TDLS conversation between two devices. + * @NL80211_ATTR_TDLS_OPERATION: High level TDLS operation; see + * &enum nl80211_tdls_operation, represented as a u8. + * @NL80211_ATTR_TDLS_SUPPORT: A flag indicating the device can operate + * as a TDLS peer sta. + * @NL80211_ATTR_TDLS_EXTERNAL_SETUP: The TDLS discovery/setup and teardown + * procedures should be performed by sending TDLS packets via + * %NL80211_CMD_TDLS_MGMT. Otherwise %NL80211_CMD_TDLS_OPER should be + * used for asking the driver to perform a TDLS operation. + * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ @@ -1311,6 +1331,12 @@ enum nl80211_attrs { NL80211_ATTR_TX_NO_CCK_RATE, + NL80211_ATTR_TDLS_ACTION, + NL80211_ATTR_TDLS_DIALOG_TOKEN, + NL80211_ATTR_TDLS_OPERATION, + NL80211_ATTR_TDLS_SUPPORT, + NL80211_ATTR_TDLS_EXTERNAL_SETUP, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -2604,4 +2630,20 @@ enum nl80211_pmksa_candidate_attr { MAX_NL80211_PMKSA_CANDIDATE = NUM_NL80211_PMKSA_CANDIDATE - 1 }; +/** + * enum nl80211_tdls_operation - values for %NL80211_ATTR_TDLS_OPERATION + * @NL80211_TDLS_DISCOVERY_REQ: Send a TDLS discovery request + * @NL80211_TDLS_SETUP: Setup TDLS link + * @NL80211_TDLS_TEARDOWN: Teardown a TDLS link which is already established + * @NL80211_TDLS_ENABLE_LINK: Enable TDLS link + * @NL80211_TDLS_DISABLE_LINK: Disable TDLS link + */ +enum nl80211_tdls_operation { + NL80211_TDLS_DISCOVERY_REQ, + NL80211_TDLS_SETUP, + NL80211_TDLS_TEARDOWN, + NL80211_TDLS_ENABLE_LINK, + NL80211_TDLS_DISABLE_LINK, +}; + #endif /* __LINUX_NL80211_H */ diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 34b8f269976b..74f4f85be32f 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1422,6 +1422,9 @@ struct cfg80211_gtk_rekey_data { * @set_ringparam: Set tx and rx ring sizes. * * @get_ringparam: Get tx and rx ring current and maximum sizes. + * + * @tdls_mgmt: Transmit a TDLS management frame. + * @tdls_oper: Perform a high-level TDLS operation (e.g. TDLS link setup). */ struct cfg80211_ops { int (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow); @@ -1605,6 +1608,12 @@ struct cfg80211_ops { int (*set_rekey_data)(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_gtk_rekey_data *data); + + int (*tdls_mgmt)(struct wiphy *wiphy, struct net_device *dev, + u8 *peer, u8 action_code, u8 dialog_token, + u16 status_code, const u8 *buf, size_t len); + int (*tdls_oper)(struct wiphy *wiphy, struct net_device *dev, + u8 *peer, enum nl80211_tdls_operation oper); }; /* @@ -1657,6 +1666,12 @@ struct cfg80211_ops { * @WIPHY_FLAG_SUPPORTS_FW_ROAM: The device supports roaming feature in the * firmware. * @WIPHY_FLAG_AP_UAPSD: The device supports uapsd on AP. + * @WIPHY_FLAG_SUPPORTS_TDLS: The device supports TDLS (802.11z) operation. + * @WIPHY_FLAG_TDLS_EXTERNAL_SETUP: The device does not handle TDLS (802.11z) + * link setup/discovery operations internally. Setup, discovery and + * teardown packets should be sent through the @NL80211_CMD_TDLS_MGMT + * command. When this flag is not set, @NL80211_CMD_TDLS_OPER should be + * used for asking the driver/firmware to perform a TDLS operation. */ enum wiphy_flags { WIPHY_FLAG_CUSTOM_REGULATORY = BIT(0), @@ -1673,6 +1688,8 @@ enum wiphy_flags { WIPHY_FLAG_ENFORCE_COMBINATIONS = BIT(12), WIPHY_FLAG_SUPPORTS_FW_ROAM = BIT(13), WIPHY_FLAG_AP_UAPSD = BIT(14), + WIPHY_FLAG_SUPPORTS_TDLS = BIT(15), + WIPHY_FLAG_TDLS_EXTERNAL_SETUP = BIT(16), }; /** diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 3799623e7f46..25a37fc951e3 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -192,6 +192,11 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { [NL80211_ATTR_ROAM_SUPPORT] = { .type = NLA_FLAG }, [NL80211_ATTR_SCHED_SCAN_MATCH] = { .type = NLA_NESTED }, [NL80211_ATTR_TX_NO_CCK_RATE] = { .type = NLA_FLAG }, + [NL80211_ATTR_TDLS_ACTION] = { .type = NLA_U8 }, + [NL80211_ATTR_TDLS_DIALOG_TOKEN] = { .type = NLA_U8 }, + [NL80211_ATTR_TDLS_OPERATION] = { .type = NLA_U8 }, + [NL80211_ATTR_TDLS_SUPPORT] = { .type = NLA_FLAG }, + [NL80211_ATTR_TDLS_EXTERNAL_SETUP] = { .type = NLA_FLAG }, }; /* policy for the key attributes */ @@ -732,9 +737,12 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, NLA_PUT_FLAG(msg, NL80211_ATTR_SUPPORT_MESH_AUTH); if (dev->wiphy.flags & WIPHY_FLAG_AP_UAPSD) NLA_PUT_FLAG(msg, NL80211_ATTR_SUPPORT_AP_UAPSD); - if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_FW_ROAM) NLA_PUT_FLAG(msg, NL80211_ATTR_ROAM_SUPPORT); + if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) + NLA_PUT_FLAG(msg, NL80211_ATTR_TDLS_SUPPORT); + if (dev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP) + NLA_PUT_FLAG(msg, NL80211_ATTR_TDLS_EXTERNAL_SETUP); NLA_PUT(msg, NL80211_ATTR_CIPHER_SUITES, sizeof(u32) * dev->wiphy.n_cipher_suites, @@ -877,6 +885,10 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, } CMD(set_channel, SET_CHANNEL); CMD(set_wds_peer, SET_WDS_PEER); + if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) { + CMD(tdls_mgmt, TDLS_MGMT); + CMD(tdls_oper, TDLS_OPER); + } if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) CMD(sched_scan_start, START_SCHED_SCAN); @@ -4966,6 +4978,57 @@ static int nl80211_flush_pmksa(struct sk_buff *skb, struct genl_info *info) return rdev->ops->flush_pmksa(&rdev->wiphy, dev); } +static int nl80211_tdls_mgmt(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + u8 action_code, dialog_token; + u16 status_code; + u8 *peer; + + if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) || + !rdev->ops->tdls_mgmt) + return -EOPNOTSUPP; + + if (!info->attrs[NL80211_ATTR_TDLS_ACTION] || + !info->attrs[NL80211_ATTR_STATUS_CODE] || + !info->attrs[NL80211_ATTR_TDLS_DIALOG_TOKEN] || + !info->attrs[NL80211_ATTR_IE] || + !info->attrs[NL80211_ATTR_MAC]) + return -EINVAL; + + peer = nla_data(info->attrs[NL80211_ATTR_MAC]); + action_code = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_ACTION]); + status_code = nla_get_u16(info->attrs[NL80211_ATTR_STATUS_CODE]); + dialog_token = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_DIALOG_TOKEN]); + + return rdev->ops->tdls_mgmt(&rdev->wiphy, dev, peer, action_code, + dialog_token, status_code, + nla_data(info->attrs[NL80211_ATTR_IE]), + nla_len(info->attrs[NL80211_ATTR_IE])); +} + +static int nl80211_tdls_oper(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + enum nl80211_tdls_operation operation; + u8 *peer; + + if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) || + !rdev->ops->tdls_oper) + return -EOPNOTSUPP; + + if (!info->attrs[NL80211_ATTR_TDLS_OPERATION] || + !info->attrs[NL80211_ATTR_MAC]) + return -EINVAL; + + operation = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_OPERATION]); + peer = nla_data(info->attrs[NL80211_ATTR_MAC]); + + return rdev->ops->tdls_oper(&rdev->wiphy, dev, peer, operation); +} + static int nl80211_remain_on_channel(struct sk_buff *skb, struct genl_info *info) { @@ -6281,6 +6344,22 @@ static struct genl_ops nl80211_ops[] = { .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | NL80211_FLAG_NEED_RTNL, }, + { + .cmd = NL80211_CMD_TDLS_MGMT, + .doit = nl80211_tdls_mgmt, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | + NL80211_FLAG_NEED_RTNL, + }, + { + .cmd = NL80211_CMD_TDLS_OPER, + .doit = nl80211_tdls_oper, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | + NL80211_FLAG_NEED_RTNL, + }, }; static struct genl_multicast_group nl80211_mlme_mcgrp = { -- cgit v1.2.3 From 768db3438b4b48a33d073093bb364e624409cab7 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Wed, 28 Sep 2011 14:12:51 +0300 Subject: mac80211: standardize adding supported rates IEs Relocate the mesh implementation of adding the (extended) supported rates IE to util.c, anticipating its use by other parts of mac80211. Signed-off-by: Arik Nemtsov Cc: Kalyan C Gaddam Signed-off-by: John W. Linville --- include/net/mac80211.h | 5 ++++ net/mac80211/mesh.c | 58 ----------------------------------------------- net/mac80211/mesh.h | 4 ---- net/mac80211/mesh_plink.c | 4 ++-- net/mac80211/tx.c | 4 ++-- net/mac80211/util.c | 57 ++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 66 insertions(+), 66 deletions(-) (limited to 'include/net') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 1e83afae3c64..b5f7ada2f87b 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -3444,4 +3444,9 @@ void ieee80211_enable_rssi_reports(struct ieee80211_vif *vif, int rssi_max_thold); void ieee80211_disable_rssi_reports(struct ieee80211_vif *vif); + +int ieee80211_add_srates_ie(struct ieee80211_vif *vif, struct sk_buff *skb); + +int ieee80211_add_ext_srates_ie(struct ieee80211_vif *vif, + struct sk_buff *skb); #endif /* MAC80211_H */ diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index a4225ae69681..a7078fdba8ca 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -320,64 +320,6 @@ mesh_add_rsn_ie(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata) return 0; } -int -mesh_add_srates_ie(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata) -{ - struct ieee80211_local *local = sdata->local; - struct ieee80211_supported_band *sband; - int rate; - u8 i, rates, *pos; - - sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; - rates = sband->n_bitrates; - if (rates > 8) - rates = 8; - - if (skb_tailroom(skb) < rates + 2) - return -ENOMEM; - - pos = skb_put(skb, rates + 2); - *pos++ = WLAN_EID_SUPP_RATES; - *pos++ = rates; - for (i = 0; i < rates; i++) { - rate = sband->bitrates[i].bitrate; - *pos++ = (u8) (rate / 5); - } - - return 0; -} - -int -mesh_add_ext_srates_ie(struct sk_buff *skb, - struct ieee80211_sub_if_data *sdata) -{ - struct ieee80211_local *local = sdata->local; - struct ieee80211_supported_band *sband; - int rate; - u8 i, exrates, *pos; - - sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; - exrates = sband->n_bitrates; - if (exrates > 8) - exrates -= 8; - else - exrates = 0; - - if (skb_tailroom(skb) < exrates + 2) - return -ENOMEM; - - if (exrates) { - pos = skb_put(skb, exrates + 2); - *pos++ = WLAN_EID_EXT_SUPP_RATES; - *pos++ = exrates; - for (i = 8; i < sband->n_bitrates; i++) { - rate = sband->bitrates[i].bitrate; - *pos++ = (u8) (rate / 5); - } - } - return 0; -} - int mesh_add_ds_params_ie(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata) { diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h index 7118e8e8855c..8c00e2d1d636 100644 --- a/net/mac80211/mesh.h +++ b/net/mac80211/mesh.h @@ -210,10 +210,6 @@ int mesh_add_rsn_ie(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata); int mesh_add_vendor_ies(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata); -int mesh_add_srates_ie(struct sk_buff *skb, - struct ieee80211_sub_if_data *sdata); -int mesh_add_ext_srates_ie(struct sk_buff *skb, - struct ieee80211_sub_if_data *sdata); int mesh_add_ds_params_ie(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata); void mesh_rmc_free(struct ieee80211_sub_if_data *sdata); diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index 1213a23ff0fa..9cc5029b3c46 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -185,8 +185,8 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata, pos = skb_put(skb, 2); memcpy(pos + 2, &plid, 2); } - if (mesh_add_srates_ie(skb, sdata) || - mesh_add_ext_srates_ie(skb, sdata) || + if (ieee80211_add_srates_ie(&sdata->vif, skb) || + ieee80211_add_ext_srates_ie(&sdata->vif, skb) || mesh_add_rsn_ie(skb, sdata) || mesh_add_meshid_ie(skb, sdata) || mesh_add_meshconf_ie(skb, sdata)) diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 7cd6c28968b2..542272acfc1a 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -2307,9 +2307,9 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, *pos++ = WLAN_EID_SSID; *pos++ = 0x0; - if (mesh_add_srates_ie(skb, sdata) || + if (ieee80211_add_srates_ie(&sdata->vif, skb) || mesh_add_ds_params_ie(skb, sdata) || - mesh_add_ext_srates_ie(skb, sdata) || + ieee80211_add_ext_srates_ie(&sdata->vif, skb) || mesh_add_rsn_ie(skb, sdata) || mesh_add_meshid_ie(skb, sdata) || mesh_add_meshconf_ie(skb, sdata) || diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 2c9dc360dc6d..9d4f14621bb0 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1364,3 +1364,60 @@ void ieee80211_disable_rssi_reports(struct ieee80211_vif *vif) _ieee80211_enable_rssi_reports(sdata, 0, 0); } EXPORT_SYMBOL(ieee80211_disable_rssi_reports); + +int ieee80211_add_srates_ie(struct ieee80211_vif *vif, struct sk_buff *skb) +{ + struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + struct ieee80211_local *local = sdata->local; + struct ieee80211_supported_band *sband; + int rate; + u8 i, rates, *pos; + + sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; + rates = sband->n_bitrates; + if (rates > 8) + rates = 8; + + if (skb_tailroom(skb) < rates + 2) + return -ENOMEM; + + pos = skb_put(skb, rates + 2); + *pos++ = WLAN_EID_SUPP_RATES; + *pos++ = rates; + for (i = 0; i < rates; i++) { + rate = sband->bitrates[i].bitrate; + *pos++ = (u8) (rate / 5); + } + + return 0; +} + +int ieee80211_add_ext_srates_ie(struct ieee80211_vif *vif, struct sk_buff *skb) +{ + struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + struct ieee80211_local *local = sdata->local; + struct ieee80211_supported_band *sband; + int rate; + u8 i, exrates, *pos; + + sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; + exrates = sband->n_bitrates; + if (exrates > 8) + exrates -= 8; + else + exrates = 0; + + if (skb_tailroom(skb) < exrates + 2) + return -ENOMEM; + + if (exrates) { + pos = skb_put(skb, exrates + 2); + *pos++ = WLAN_EID_EXT_SUPP_RATES; + *pos++ = exrates; + for (i = 8; i < sband->n_bitrates; i++) { + rate = sband->bitrates[i].bitrate; + *pos++ = (u8) (rate / 5); + } + } + return 0; +} -- cgit v1.2.3 From 042ec4533720122e6cb93dd9f3b6a75fe2fcff16 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 29 Sep 2011 16:04:26 +0200 Subject: mac80211: let drivers inform it about per TID buffered frames For uAPSD implementation, it is necessary to know on which ACs frames are buffered. mac80211 obviously knows about the frames it has buffered itself, but with aggregation many drivers buffer frames. Thus, mac80211 needs to be informed about this. For now, since we don't have APSD in any form, this will unconditionally set the TIM bit for the station but later with uAPSD only some ACs might cause the TIM bit to be set. ath9k is the only driver using this API and I only modify it in the most basic way, it won't be able to implement uAPSD with this yet. But it can't do that anyway since there's no way to selectively release frames to the peer yet. Since drivers will buffer frames per TID, let them inform mac80211 on a per TID basis, mac80211 will then sort out the AC mapping itself. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ath9k.h | 3 ++- drivers/net/wireless/ath/ath9k/main.c | 3 +-- drivers/net/wireless/ath/ath9k/xmit.c | 14 +++++++------- include/net/mac80211.h | 34 ++++++++++++++++++++++++++-------- net/mac80211/sta_info.c | 8 ++++++-- 5 files changed, 42 insertions(+), 20 deletions(-) (limited to 'include/net') diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 94d887b65e69..1e8614783181 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -340,7 +340,8 @@ void ath_tx_aggr_stop(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid); void ath_tx_aggr_resume(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid); void ath_tx_aggr_wakeup(struct ath_softc *sc, struct ath_node *an); -bool ath_tx_aggr_sleep(struct ath_softc *sc, struct ath_node *an); +void ath_tx_aggr_sleep(struct ieee80211_sta *sta, struct ath_softc *sc, + struct ath_node *an); /********/ /* VIFs */ diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index edaa7843bf4c..0ebf7321df12 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -1833,8 +1833,7 @@ static void ath9k_sta_notify(struct ieee80211_hw *hw, switch (cmd) { case STA_NOTIFY_SLEEP: an->sleeping = true; - if (ath_tx_aggr_sleep(sc, an)) - ieee80211_sta_set_tim(sta); + ath_tx_aggr_sleep(sta, sc, an); break; case STA_NOTIFY_AWAKE: an->sleeping = false; diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index f5d4764888b9..c2bfc57958d8 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -542,7 +542,7 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq, /* prepend un-acked frames to the beginning of the pending frame queue */ if (!skb_queue_empty(&bf_pending)) { if (an->sleeping) - ieee80211_sta_set_tim(sta); + ieee80211_sta_set_buffered(sta, tid->tidno, true); spin_lock_bh(&txq->axq_lock); if (clear_filter) @@ -1153,12 +1153,13 @@ void ath_tx_aggr_stop(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid) ath_tx_flush_tid(sc, txtid); } -bool ath_tx_aggr_sleep(struct ath_softc *sc, struct ath_node *an) +void ath_tx_aggr_sleep(struct ieee80211_sta *sta, struct ath_softc *sc, + struct ath_node *an) { struct ath_atx_tid *tid; struct ath_atx_ac *ac; struct ath_txq *txq; - bool buffered = false; + bool buffered; int tidno; for (tidno = 0, tid = &an->tid[tidno]; @@ -1172,8 +1173,7 @@ bool ath_tx_aggr_sleep(struct ath_softc *sc, struct ath_node *an) spin_lock_bh(&txq->axq_lock); - if (!skb_queue_empty(&tid->buf_q)) - buffered = true; + buffered = !skb_queue_empty(&tid->buf_q); tid->sched = false; list_del(&tid->list); @@ -1184,9 +1184,9 @@ bool ath_tx_aggr_sleep(struct ath_softc *sc, struct ath_node *an) } spin_unlock_bh(&txq->axq_lock); - } - return buffered; + ieee80211_sta_set_buffered(sta, tidno, buffered); + } } void ath_tx_aggr_wakeup(struct ath_softc *sc, struct ath_node *an) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index b5f7ada2f87b..e66638e749c6 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -2361,17 +2361,35 @@ static inline int ieee80211_sta_ps_transition_ni(struct ieee80211_sta *sta, #define IEEE80211_TX_STATUS_HEADROOM 13 /** - * ieee80211_sta_set_tim - set the TIM bit for a sleeping station + * ieee80211_sta_set_buffered - inform mac80211 about driver-buffered frames * @sta: &struct ieee80211_sta pointer for the sleeping station + * @tid: the TID that has buffered frames + * @buffered: indicates whether or not frames are buffered for this TID * * If a driver buffers frames for a powersave station instead of passing - * them back to mac80211 for retransmission, the station needs to be told - * to wake up using the TIM bitmap in the beacon. - * - * This function sets the station's TIM bit - it will be cleared when the - * station wakes up. - */ -void ieee80211_sta_set_tim(struct ieee80211_sta *sta); + * them back to mac80211 for retransmission, the station may still need + * to be told that there are buffered frames via the TIM bit. + * + * This function informs mac80211 whether or not there are frames that are + * buffered in the driver for a given TID; mac80211 can then use this data + * to set the TIM bit (NOTE: This may call back into the driver's set_tim + * call! Beware of the locking!) + * + * If all frames are released to the station (due to PS-poll or uAPSD) + * then the driver needs to inform mac80211 that there no longer are + * frames buffered. However, when the station wakes up mac80211 assumes + * that all buffered frames will be transmitted and clears this data, + * drivers need to make sure they inform mac80211 about all buffered + * frames on the sleep transition (sta_notify() with %STA_NOTIFY_SLEEP). + * + * Note that technically mac80211 only needs to know this per AC, not per + * TID, but since driver buffering will inevitably happen per TID (since + * it is related to aggregation) it is easier to make mac80211 map the + * TID to the AC as required instead of keeping track in all drivers that + * use this API. + */ +void ieee80211_sta_set_buffered(struct ieee80211_sta *sta, + u8 tid, bool buffered); /** * ieee80211_tx_status - transmit status callback diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index c52e58c0a979..016742d4c48e 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -1117,11 +1117,15 @@ void ieee80211_sta_block_awake(struct ieee80211_hw *hw, } EXPORT_SYMBOL(ieee80211_sta_block_awake); -void ieee80211_sta_set_tim(struct ieee80211_sta *pubsta) +void ieee80211_sta_set_buffered(struct ieee80211_sta *pubsta, + u8 tid, bool buffered) { struct sta_info *sta = container_of(pubsta, struct sta_info, sta); + if (!buffered) + return; + set_sta_flags(sta, WLAN_STA_PS_DRIVER_BUF); sta_info_set_tim_bit(sta); } -EXPORT_SYMBOL(ieee80211_sta_set_tim); +EXPORT_SYMBOL(ieee80211_sta_set_buffered); -- cgit v1.2.3 From 948d887dec1042a7d78ae311908113e26502062f Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 29 Sep 2011 16:04:29 +0200 Subject: mac80211: split PS buffers into ACs For uAPSD support we'll need to have per-AC PS buffers. As this is a major undertaking, split the buffers before really adding support for uAPSD. This already makes some reference to the uapsd_queues variable, but for now that will never be non-zero. Since book-keeping is complicated, also change the logic for keeping a maximum of frames only and allow 64 frames per AC (up from 128 for a station). Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- include/net/mac80211.h | 1 + net/mac80211/debugfs_sta.c | 10 ++- net/mac80211/sta_info.c | 204 ++++++++++++++++++++++++++++++++++----------- net/mac80211/sta_info.h | 24 +++--- net/mac80211/status.c | 17 +++- net/mac80211/tx.c | 42 ++++++---- 6 files changed, 214 insertions(+), 84 deletions(-) (limited to 'include/net') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index e66638e749c6..acf9eaf59641 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -109,6 +109,7 @@ enum ieee80211_ac_numbers { IEEE80211_AC_BE = 2, IEEE80211_AC_BK = 3, }; +#define IEEE80211_NUM_ACS 4 /** * struct ieee80211_tx_queue_params - transmit queue configuration diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c index a01d2137fddc..20ec2b0cb3c1 100644 --- a/net/mac80211/debugfs_sta.c +++ b/net/mac80211/debugfs_sta.c @@ -78,8 +78,14 @@ static ssize_t sta_num_ps_buf_frames_read(struct file *file, size_t count, loff_t *ppos) { struct sta_info *sta = file->private_data; - return mac80211_format_buffer(userbuf, count, ppos, "%u\n", - skb_queue_len(&sta->ps_tx_buf)); + char buf[17*IEEE80211_NUM_ACS], *p = buf; + int ac; + + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) + p += scnprintf(p, sizeof(buf)+buf-p, "AC%d: %d\n", ac, + skb_queue_len(&sta->ps_tx_buf[ac]) + + skb_queue_len(&sta->tx_filtered[ac])); + return simple_read_from_buffer(userbuf, count, ppos, buf, p - buf); } STA_OPS(num_ps_buf_frames); diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 8dabe66fc37f..4d85672f0b8f 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -309,8 +309,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,12 +643,32 @@ static inline void __bss_tim_clear(struct ieee80211_if_ap *bss, u16 aid) bss->tim[aid / 8] &= ~(1 << (aid % 8)); } +static unsigned long ieee80211_tids_for_ac(int ac) +{ + /* 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_recalc_tim(struct sta_info *sta) { struct ieee80211_local *local = sta->local; struct ieee80211_if_ap *bss = sta->sdata->bss; unsigned long flags; - bool have_data = false; + bool indicate_tim = false; + u8 ignore_for_tim = sta->sta.uapsd_queues; + int ac; if (WARN_ON_ONCE(!sta->sdata->bss)) return; @@ -658,21 +680,43 @@ void sta_info_recalc_tim(struct sta_info *sta) if (sta->dead) goto done; - have_data = test_sta_flags(sta, WLAN_STA_PS_DRIVER_BUF) || - !skb_queue_empty(&sta->tx_filtered) || - !skb_queue_empty(&sta->ps_tx_buf); + /* + * 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; + + 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; + + tids = ieee80211_tids_for_ac(ac); + + indicate_tim |= + sta->driver_buffered_tids & tids; + } done: spin_lock_irqsave(&local->sta_lock, flags); - if (have_data) + 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, have_data); + drv_set_tim(local, &sta->sta, indicate_tim); local->tim_in_locked_section = false; } @@ -699,16 +743,12 @@ 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; - /* This is only necessary for stations on BSS interfaces */ - if (!sta->sdata->bss) - return false; - /* * First check for frames that should expire on the filtered * queue. Frames here were rejected by the driver and are on @@ -717,13 +757,13 @@ static bool sta_info_cleanup_expire_buffered(struct ieee80211_local *local, * total_ps_buffered counter. */ for (;;) { - spin_lock_irqsave(&sta->tx_filtered.lock, flags); - skb = skb_peek(&sta->tx_filtered); + 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); + skb = __skb_dequeue(&sta->tx_filtered[ac]); else skb = NULL; - spin_unlock_irqrestore(&sta->tx_filtered.lock, flags); + spin_unlock_irqrestore(&sta->tx_filtered[ac].lock, flags); /* * Frames are queued in order, so if this one @@ -743,13 +783,13 @@ static bool sta_info_cleanup_expire_buffered(struct ieee80211_local *local, * 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 @@ -779,8 +819,25 @@ static bool sta_info_cleanup_expire_buffered(struct ieee80211_local *local, * 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) && - skb_queue_empty(&sta->tx_filtered)); + 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) @@ -788,7 +845,7 @@ static int __must_check __sta_info_destroy(struct sta_info *sta) struct ieee80211_local *local; struct ieee80211_sub_if_data *sdata; unsigned long flags; - int ret, i; + int ret, i, ac; might_sleep(); @@ -856,9 +913,11 @@ static int __must_check __sta_info_destroy(struct sta_info *sta) */ synchronize_rcu(); - local->total_ps_buffered -= skb_queue_len(&sta->ps_tx_buf); - __skb_queue_purge(&sta->ps_tx_buf); - __skb_queue_purge(&sta->tx_filtered); + 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)) @@ -1055,17 +1114,33 @@ 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; + + 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); + 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); @@ -1073,7 +1148,7 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *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 */ } @@ -1081,17 +1156,43 @@ void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta) { struct ieee80211_sub_if_data *sdata = sta->sdata; struct ieee80211_local *local = sdata->local; - struct sk_buff *skb; - int no_pending_pkts; + struct sk_buff *skb = NULL; + bool more_data = false; + int ac; + u8 ignore_for_response = sta->sta.uapsd_queues; - skb = skb_dequeue(&sta->tx_filtered); - if (!skb) { - skb = skb_dequeue(&sta->ps_tx_buf); - if (skb) - local->total_ps_buffered--; + /* + * 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; + + /* + * Get response frame and more data bit for it. + */ + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { + if (ignore_for_response & BIT(ac)) + continue; + + if (!skb) { + skb = skb_dequeue(&sta->tx_filtered[ac]); + if (!skb) { + skb = skb_dequeue(&sta->ps_tx_buf[ac]); + if (skb) + local->total_ps_buffered--; + } + } + + /* FIXME: take into account driver-buffered frames */ + + if (!skb_queue_empty(&sta->tx_filtered[ac]) || + !skb_queue_empty(&sta->ps_tx_buf[ac])) { + more_data = true; + break; + } } - 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); @@ -1105,14 +1206,13 @@ void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta) 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)); + printk(KERN_DEBUG "STA %pM aid %d: PS Poll\n", + sta->sta.addr, sta->sta.aid); #endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */ /* Use MoreData flag to indicate whether there are more * buffered frames for this STA */ - if (no_pending_pkts) + if (!more_data) hdr->frame_control &= cpu_to_le16(~IEEE80211_FCTL_MOREDATA); else hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREDATA); @@ -1154,10 +1254,14 @@ void ieee80211_sta_set_buffered(struct ieee80211_sta *pubsta, { struct sta_info *sta = container_of(pubsta, struct sta_info, sta); - if (!buffered) + if (WARN_ON(tid >= STA_TID_NUM)) return; - set_sta_flags(sta, WLAN_STA_PS_DRIVER_BUF); + 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_buffered); diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index c9ffb7ce636b..8589afad3295 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -43,8 +43,6 @@ * be in the queues * @WLAN_STA_PSPOLL: Station sent PS-poll while driver was keeping * station in power-save mode, reply when the driver unblocks. - * @WLAN_STA_PS_DRIVER_BUF: Station has frames pending in driver internal - * buffers. Automatically cleared on station wake-up. * @WLAN_STA_TDLS_PEER: Station is a TDLS peer. * @WLAN_STA_TDLS_PEER_AUTH: This TDLS peer is authorized to send direct * packets. This means the link is enabled. @@ -63,7 +61,6 @@ enum ieee80211_sta_info_flags { WLAN_STA_BLOCK_BA = 1<<11, WLAN_STA_PS_DRIVER = 1<<12, WLAN_STA_PSPOLL = 1<<13, - WLAN_STA_PS_DRIVER_BUF = 1<<14, WLAN_STA_TDLS_PEER = 1<<15, WLAN_STA_TDLS_PEER_AUTH = 1<<16, }; @@ -212,11 +209,13 @@ struct sta_ampdu_mlme { * @drv_unblock_wk: used for driver PS unblocking * @listen_interval: listen interval of this station, when we're acting as AP * @flags: STA flags, see &enum ieee80211_sta_info_flags - * @ps_tx_buf: buffer of frames to transmit to this station - * when it leaves power saving state - * @tx_filtered: buffer of frames we already tried to transmit - * but were filtered by hardware due to STA having entered - * power saving state + * @ps_tx_buf: buffers (per AC) of frames to transmit to this station + * when it leaves power saving state or polls + * @tx_filtered: buffers (per AC) of frames we already tried to + * transmit but were filtered by hardware due to STA having + * entered power saving state, these are also delivered to + * the station when it leaves powersave or polls for frames + * @driver_buffered_tids: bitmap of TIDs the driver has data buffered on * @rx_packets: Number of MSDUs received from this STA * @rx_bytes: Number of bytes received from this STA * @wep_weak_iv_count: number of weak WEP IVs received from this station @@ -286,8 +285,9 @@ struct sta_info { * STA powersave frame queues, no more than the internal * locking required. */ - struct sk_buff_head ps_tx_buf; - struct sk_buff_head tx_filtered; + struct sk_buff_head ps_tx_buf[IEEE80211_NUM_ACS]; + struct sk_buff_head tx_filtered[IEEE80211_NUM_ACS]; + unsigned long driver_buffered_tids; /* Updated from RX path only, no locking requirements */ unsigned long rx_packets, rx_bytes; @@ -434,8 +434,8 @@ rcu_dereference_protected_tid_tx(struct sta_info *sta, int tid) #define STA_HASH(sta) (sta[5]) -/* Maximum number of frames to buffer per power saving station */ -#define STA_MAX_TX_BUFFER 128 +/* Maximum number of frames to buffer per power saving station per AC */ +#define STA_MAX_TX_BUFFER 64 /* Minimum buffered frame expiry time. If STA uses listen interval that is * smaller than this value, the minimum value here is used instead. */ diff --git a/net/mac80211/status.c b/net/mac80211/status.c index 783542a8ea20..c06857bbd573 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c @@ -14,6 +14,7 @@ #include "rate.h" #include "mesh.h" #include "led.h" +#include "wme.h" void ieee80211_tx_status_irqsafe(struct ieee80211_hw *hw, @@ -43,6 +44,8 @@ static void ieee80211_handle_filtered_frame(struct ieee80211_local *local, struct sk_buff *skb) { struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_hdr *hdr = (void *)skb->data; + int ac; /* * This skb 'survived' a round-trip through the driver, and @@ -62,6 +65,14 @@ static void ieee80211_handle_filtered_frame(struct ieee80211_local *local, sta->tx_filtered_count++; + if (ieee80211_is_data_qos(hdr->frame_control)) { + int tid = *ieee80211_get_qos_ctl(hdr) & + IEEE80211_QOS_CTL_TID_MASK; + ac = ieee802_1d_to_ac[tid & 7]; + } else { + ac = IEEE80211_AC_BE; + } + /* * Clear the TX filter mask for this STA when sending the next * packet. If the STA went to power save mode, this will happen @@ -104,8 +115,8 @@ static void ieee80211_handle_filtered_frame(struct ieee80211_local *local, * unknown. */ if (test_sta_flags(sta, WLAN_STA_PS_STA) && - skb_queue_len(&sta->tx_filtered) < STA_MAX_TX_BUFFER) { - skb_queue_tail(&sta->tx_filtered, skb); + skb_queue_len(&sta->tx_filtered[ac]) < STA_MAX_TX_BUFFER) { + skb_queue_tail(&sta->tx_filtered[ac], skb); sta_info_recalc_tim(sta); if (!timer_pending(&local->sta_cleanup)) @@ -127,7 +138,7 @@ static void ieee80211_handle_filtered_frame(struct ieee80211_local *local, if (net_ratelimit()) wiphy_debug(local->hw.wiphy, "dropped TX filtered frame, queue_len=%d PS=%d @%lu\n", - skb_queue_len(&sta->tx_filtered), + skb_queue_len(&sta->tx_filtered[ac]), !!test_sta_flags(sta, WLAN_STA_PS_STA), jiffies); #endif dev_kfree_skb(skb); diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index d6754908ff79..a1029449df44 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -343,13 +343,22 @@ static void purge_old_ps_buffers(struct ieee80211_local *local) total += skb_queue_len(&ap->ps_bc_buf); } + /* + * Drop one frame from each station from the lowest-priority + * AC that has frames at all. + */ list_for_each_entry_rcu(sta, &local->sta_list, list) { - skb = skb_dequeue(&sta->ps_tx_buf); - if (skb) { - purged++; - dev_kfree_skb(skb); + int ac; + + for (ac = IEEE80211_AC_BK; ac >= IEEE80211_AC_VO; ac--) { + skb = skb_dequeue(&sta->ps_tx_buf[ac]); + total += skb_queue_len(&sta->ps_tx_buf[ac]); + if (skb) { + purged++; + dev_kfree_skb(skb); + break; + } } - total += skb_queue_len(&sta->ps_tx_buf); } rcu_read_unlock(); @@ -448,22 +457,21 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx) if (unlikely((staflags & (WLAN_STA_PS_STA | WLAN_STA_PS_DRIVER)) && !(info->flags & IEEE80211_TX_CTL_PSPOLL_RESPONSE))) { + int ac = skb_get_queue_mapping(tx->skb); + #ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG - printk(KERN_DEBUG "STA %pM aid %d: PS buffer (entries " - "before %d)\n", - sta->sta.addr, sta->sta.aid, - skb_queue_len(&sta->ps_tx_buf)); + printk(KERN_DEBUG "STA %pM aid %d: PS buffer for AC %d\n", + sta->sta.addr, sta->sta.aid, ac); #endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */ if (tx->local->total_ps_buffered >= TOTAL_MAX_TX_BUFFER) purge_old_ps_buffers(tx->local); - if (skb_queue_len(&sta->ps_tx_buf) >= STA_MAX_TX_BUFFER) { - struct sk_buff *old = skb_dequeue(&sta->ps_tx_buf); + if (skb_queue_len(&sta->ps_tx_buf[ac]) >= STA_MAX_TX_BUFFER) { + struct sk_buff *old = skb_dequeue(&sta->ps_tx_buf[ac]); #ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG - if (net_ratelimit()) { - printk(KERN_DEBUG "%s: STA %pM TX " - "buffer full - dropping oldest frame\n", - tx->sdata->name, sta->sta.addr); - } + if (net_ratelimit()) + printk(KERN_DEBUG "%s: STA %pM TX buffer for " + "AC %d full - dropping oldest frame\n", + tx->sdata->name, sta->sta.addr, ac); #endif dev_kfree_skb(old); } else @@ -472,7 +480,7 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx) info->control.jiffies = jiffies; info->control.vif = &tx->sdata->vif; info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING; - skb_queue_tail(&sta->ps_tx_buf, tx->skb); + skb_queue_tail(&sta->ps_tx_buf[ac], tx->skb); if (!timer_pending(&local->sta_cleanup)) mod_timer(&local->sta_cleanup, -- cgit v1.2.3 From 4049e09acdf4ffd270cb8fbf1cf5b39c3d02357c Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 29 Sep 2011 16:04:32 +0200 Subject: mac80211: allow releasing driver-buffered frames If there are frames for a station buffered in the driver, mac80211 announces those in the TIM IE but there's no way to release them. Add new API to release such frames and use it when the station polls for a frame. Since the API will soon also be used for uAPSD it is easily extensible. Note that before this change drivers announcing driver-buffered frames in the TIM bit actually will respond to a PS-Poll with a potentially lower priority frame (if there are any frames buffered in mac80211), after this patch a driver that hasn't been changed will no longer respond at all. This only affects ath9k, which will need to be fixed to implement the new API. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- include/net/mac80211.h | 31 +++++++++++++++++ net/mac80211/driver-ops.h | 15 +++++++++ net/mac80211/driver-trace.h | 35 +++++++++++++++++++ net/mac80211/sta_info.c | 82 ++++++++++++++++++++++++++++++++++++--------- 4 files changed, 147 insertions(+), 16 deletions(-) (limited to 'include/net') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index acf9eaf59641..dc2ea46eb5d6 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1621,6 +1621,14 @@ enum ieee80211_tx_sync_type { IEEE80211_TX_SYNC_ACTION, }; +/** + * enum ieee80211_frame_release_type - frame release reason + * @IEEE80211_FRAME_RELEASE_PSPOLL: frame released for PS-Poll + */ +enum ieee80211_frame_release_type { + IEEE80211_FRAME_RELEASE_PSPOLL, +}; + /** * struct ieee80211_ops - callbacks from mac80211 to the driver * @@ -1931,6 +1939,23 @@ enum ieee80211_tx_sync_type { * The callback can sleep. * @rssi_callback: Notify driver when the average RSSI goes above/below * thresholds that were registered previously. The callback can sleep. + * + * @release_buffered_frames: Release buffered frames according to the given + * parameters. In the case where the driver buffers some frames for + * sleeping stations mac80211 will use this callback to tell the driver + * to release some frames, either for PS-poll or uAPSD. + * Note that if the @more_data paramter is %false the driver must check + * if there are more frames on the given TIDs, and if there are more than + * the frames being released then it must still set the more-data bit in + * the frame. If the @more_data parameter is %true, then of course the + * more-data bit must always be set. + * The @tids parameter tells the driver which TIDs to release frames + * from, for PS-poll it will always have only a single bit set. + * In the case this is used for uAPSD, the @num_frames parameter may be + * bigger than one, but the driver may send fewer frames (it must send + * at least one, however). In this case it is also responsible for + * setting the EOSP flag in the QoS header of the frames. + * This callback must be atomic. */ struct ieee80211_ops { void (*tx)(struct ieee80211_hw *hw, struct sk_buff *skb); @@ -2045,6 +2070,12 @@ struct ieee80211_ops { const struct cfg80211_bitrate_mask *mask); void (*rssi_callback)(struct ieee80211_hw *hw, enum ieee80211_rssi_event rssi_event); + + void (*release_buffered_frames)(struct ieee80211_hw *hw, + struct ieee80211_sta *sta, + u16 tids, int num_frames, + enum ieee80211_frame_release_type reason, + bool more_data); }; /** diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 4f845c0845ee..8fa0d2edf54c 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -670,4 +670,19 @@ static inline void drv_rssi_callback(struct ieee80211_local *local, local->ops->rssi_callback(&local->hw, event); trace_drv_return_void(local); } + +static inline void +drv_release_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_release_buffered_frames(local, &sta->sta, tids, num_frames, + reason, more_data); + if (local->ops->release_buffered_frames) + local->ops->release_buffered_frames(&local->hw, &sta->sta, tids, + num_frames, reason, + more_data); + trace_drv_return_void(local); +} #endif /* __MAC80211_DRIVER_OPS */ diff --git a/net/mac80211/driver-trace.h b/net/mac80211/driver-trace.h index a46b279bbbe4..531fbd086794 100644 --- a/net/mac80211/driver-trace.h +++ b/net/mac80211/driver-trace.h @@ -1129,6 +1129,41 @@ TRACE_EVENT(drv_rssi_callback, ) ); +TRACE_EVENT(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), + + TP_STRUCT__entry( + LOCAL_ENTRY + STA_ENTRY + __field(u16, tids) + __field(int, num_frames) + __field(int, reason) + __field(bool, more_data) + ), + + TP_fast_assign( + LOCAL_ASSIGN; + STA_ASSIGN; + __entry->tids = tids; + __entry->num_frames = num_frames; + __entry->reason = reason; + __entry->more_data = more_data; + ), + + TP_printk( + LOCAL_PR_FMT STA_PR_FMT + " TIDs:0x%.4x frames:%d reason:%d more:%d", + LOCAL_PR_ARG, STA_PR_ARG, __entry->tids, __entry->num_frames, + __entry->reason, __entry->more_data + ) +); + /* * Tracing for API calls that drivers call. */ diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 4d85672f0b8f..b3f841948c09 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -1157,8 +1157,10 @@ void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta) struct ieee80211_sub_if_data *sdata = sta->sdata; struct ieee80211_local *local = sdata->local; struct sk_buff *skb = NULL; + bool found = false; bool more_data = false; int ac; + unsigned long driver_release_tids = 0; u8 ignore_for_response = sta->sta.uapsd_queues; /* @@ -1173,19 +1175,40 @@ void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta) * Get response frame and more data bit for it. */ for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { + unsigned long tids; + if (ignore_for_response & BIT(ac)) continue; - if (!skb) { - skb = skb_dequeue(&sta->tx_filtered[ac]); - if (!skb) { - skb = skb_dequeue(&sta->ps_tx_buf[ac]); + tids = ieee80211_tids_for_ac(ac); + + if (!found) { + driver_release_tids = sta->driver_buffered_tids & tids; + if (driver_release_tids) { + found = true; + } else { + 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) - local->total_ps_buffered--; + found = true; } - } - /* FIXME: take into account driver-buffered frames */ + /* + * 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 (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])) { @@ -1194,6 +1217,22 @@ void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta) } } + if (!found) { +#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG + /* + * 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? + */ + 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 */ + + return; + } + if (skb) { struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_hdr *hdr = @@ -1220,18 +1259,29 @@ void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta) ieee80211_add_pending_skb(local, skb); sta_info_recalc_tim(sta); -#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG } 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, + 1, IEEE80211_FRAME_RELEASE_PSPOLL, + 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 */ } } -- cgit v1.2.3 From 47086fc51aa2220f58049704a8b73e4fcdf372b9 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 29 Sep 2011 16:04:33 +0200 Subject: mac80211: implement uAPSD Add uAPSD support to mac80211. This is probably not possible with all devices, so advertising it with the cfg80211 flag will be left up to drivers that want it. Due to my previous patches it is now a fairly straight-forward extension. Drivers need to have accurate TX status reporting for the EOSP frame. For drivers that buffer themselves, the provided APIs allow releasing the right number of frames, but then drivers need to set EOSP and more-data themselves. This is documented in more detail in the new code itself. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- drivers/net/wireless/iwlegacy/iwl-4965-tx.c | 2 +- drivers/net/wireless/iwlwifi/iwl-agn-tx.c | 2 +- drivers/net/wireless/p54/txrx.c | 2 +- include/net/mac80211.h | 24 ++-- net/mac80211/rx.c | 102 ++++++++++++----- net/mac80211/sta_info.c | 170 ++++++++++++++++++++-------- net/mac80211/sta_info.h | 8 ++ net/mac80211/status.c | 15 ++- net/mac80211/tx.c | 8 +- 9 files changed, 244 insertions(+), 89 deletions(-) (limited to 'include/net') diff --git a/drivers/net/wireless/iwlegacy/iwl-4965-tx.c b/drivers/net/wireless/iwlegacy/iwl-4965-tx.c index ac4f64de1363..7f12e3638bae 100644 --- a/drivers/net/wireless/iwlegacy/iwl-4965-tx.c +++ b/drivers/net/wireless/iwlegacy/iwl-4965-tx.c @@ -335,7 +335,7 @@ int iwl4965_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) sta_priv = (void *)sta->drv_priv; if (sta_priv && sta_priv->asleep && - (info->flags & IEEE80211_TX_CTL_PSPOLL_RESPONSE)) { + (info->flags & IEEE80211_TX_CTL_POLL_RESPONSE)) { /* * This sends an asynchronous command to the device, * but we can rely on it being processed before the diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c index 0e5d6498be21..dcb3bd67d4f9 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c @@ -300,7 +300,7 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) sta_priv = (void *)info->control.sta->drv_priv; if (sta_priv && sta_priv->asleep && - (info->flags & IEEE80211_TX_CTL_PSPOLL_RESPONSE)) { + (info->flags & IEEE80211_TX_CTL_POLL_RESPONSE)) { /* * This sends an asynchronous command to the device, * but we can rely on it being processed before the diff --git a/drivers/net/wireless/p54/txrx.c b/drivers/net/wireless/p54/txrx.c index 2b97a89e7ff8..f485784a60ae 100644 --- a/drivers/net/wireless/p54/txrx.c +++ b/drivers/net/wireless/p54/txrx.c @@ -689,7 +689,7 @@ static void p54_tx_80211_header(struct p54_common *priv, struct sk_buff *skb, if (!(info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ)) *flags |= P54_HDR_FLAG_DATA_OUT_SEQNR; - if (info->flags & IEEE80211_TX_CTL_PSPOLL_RESPONSE) + if (info->flags & IEEE80211_TX_CTL_POLL_RESPONSE) *flags |= P54_HDR_FLAG_DATA_OUT_NOCANCEL; if (info->flags & IEEE80211_TX_CTL_CLEAR_PS_FILT) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index dc2ea46eb5d6..dd07964ef154 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -339,9 +339,9 @@ struct ieee80211_bss_conf { * used to indicate that a frame was already retried due to PS * @IEEE80211_TX_INTFL_DONT_ENCRYPT: completely internal to mac80211, * used to indicate frame should not be encrypted - * @IEEE80211_TX_CTL_PSPOLL_RESPONSE: (internal?) - * This frame is a response to a PS-poll frame and should be sent - * although the station is in powersave mode. + * @IEEE80211_TX_CTL_POLL_RESPONSE: This frame is a response to a poll + * frame (PS-Poll or uAPSD) and should be sent although the station + * is in powersave mode. * @IEEE80211_TX_CTL_MORE_FRAMES: More frames will be passed to the * transmit function after the current frame, this can be used * by drivers to kick the DMA queue only if unset or when the @@ -367,6 +367,10 @@ struct ieee80211_bss_conf { * @IEEE80211_TX_CTL_NO_CCK_RATE: This frame will be sent at non CCK rate. * This flag is actually used for management frame especially for P2P * frames not being sent at CCK rate in 2GHz band. + * @IEEE80211_TX_STATUS_EOSP: This packet marks the end of service period, + * when its status is reported the service period ends. For frames in + * an SP that mac80211 transmits, it is already set; for driver frames + * the driver may set this flag. * * Note: If you have to add new flags to the enumeration, then don't * forget to update %IEEE80211_TX_TEMPORARY_FLAGS when necessary. @@ -388,7 +392,7 @@ enum mac80211_tx_control_flags { IEEE80211_TX_INTFL_NEED_TXPROCESSING = BIT(14), IEEE80211_TX_INTFL_RETRIED = BIT(15), IEEE80211_TX_INTFL_DONT_ENCRYPT = BIT(16), - IEEE80211_TX_CTL_PSPOLL_RESPONSE = BIT(17), + IEEE80211_TX_CTL_POLL_RESPONSE = BIT(17), IEEE80211_TX_CTL_MORE_FRAMES = BIT(18), IEEE80211_TX_INTFL_RETRANSMISSION = BIT(19), IEEE80211_TX_INTFL_HAS_RADIOTAP = BIT(20), @@ -398,6 +402,7 @@ enum mac80211_tx_control_flags { IEEE80211_TX_CTL_TX_OFFCHAN = BIT(25), IEEE80211_TX_INTFL_TKIP_MIC_FAILURE = BIT(26), IEEE80211_TX_CTL_NO_CCK_RATE = BIT(27), + IEEE80211_TX_STATUS_EOSP = BIT(28), }; #define IEEE80211_TX_CTL_STBC_SHIFT 23 @@ -411,9 +416,9 @@ enum mac80211_tx_control_flags { IEEE80211_TX_CTL_SEND_AFTER_DTIM | IEEE80211_TX_CTL_AMPDU | \ IEEE80211_TX_STAT_TX_FILTERED | IEEE80211_TX_STAT_ACK | \ IEEE80211_TX_STAT_AMPDU | IEEE80211_TX_STAT_AMPDU_NO_BACK | \ - IEEE80211_TX_CTL_RATE_CTRL_PROBE | IEEE80211_TX_CTL_PSPOLL_RESPONSE | \ + IEEE80211_TX_CTL_RATE_CTRL_PROBE | IEEE80211_TX_CTL_POLL_RESPONSE | \ IEEE80211_TX_CTL_MORE_FRAMES | IEEE80211_TX_CTL_LDPC | \ - IEEE80211_TX_CTL_STBC) + IEEE80211_TX_CTL_STBC | IEEE80211_TX_STATUS_EOSP) /** * enum mac80211_rate_control_flags - per-rate flags set by the @@ -1624,9 +1629,12 @@ enum ieee80211_tx_sync_type { /** * enum ieee80211_frame_release_type - frame release reason * @IEEE80211_FRAME_RELEASE_PSPOLL: frame released for PS-Poll + * @IEEE80211_FRAME_RELEASE_UAPSD: frame(s) released due to + * frame received on trigger-enabled AC */ enum ieee80211_frame_release_type { IEEE80211_FRAME_RELEASE_PSPOLL, + IEEE80211_FRAME_RELEASE_UAPSD, }; /** @@ -1954,7 +1962,9 @@ enum ieee80211_frame_release_type { * In the case this is used for uAPSD, the @num_frames parameter may be * bigger than one, but the driver may send fewer frames (it must send * at least one, however). In this case it is also responsible for - * setting the EOSP flag in the QoS header of the frames. + * 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. * This callback must be atomic. */ struct ieee80211_ops { diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index db46601e50bf..9a703f00b5fb 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -1162,6 +1162,79 @@ int ieee80211_sta_ps_transition(struct ieee80211_sta *sta, bool start) } EXPORT_SYMBOL(ieee80211_sta_ps_transition); +static ieee80211_rx_result debug_noinline +ieee80211_rx_h_uapsd_and_pspoll(struct ieee80211_rx_data *rx) +{ + struct ieee80211_sub_if_data *sdata = rx->sdata; + struct ieee80211_hdr *hdr = (void *)rx->skb->data; + struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb); + int tid, ac; + + if (!rx->sta || !(status->rx_flags & IEEE80211_RX_RA_MATCH)) + return RX_CONTINUE; + + if (sdata->vif.type != NL80211_IFTYPE_AP && + sdata->vif.type != NL80211_IFTYPE_AP_VLAN) + return RX_CONTINUE; + + /* + * The device handles station powersave, so don't do anything about + * uAPSD and PS-Poll frames (the latter shouldn't even come up from + * it to mac80211 since they're handled.) + */ + if (sdata->local->hw.flags & IEEE80211_HW_AP_LINK_PS) + return RX_CONTINUE; + + /* + * Don't do anything if the station isn't already asleep. In + * the uAPSD case, the station will probably be marked asleep, + * in the PS-Poll case the station must be confused ... + */ + if (!test_sta_flags(rx->sta, WLAN_STA_PS_STA)) + return RX_CONTINUE; + + if (unlikely(ieee80211_is_pspoll(hdr->frame_control))) { + if (!test_sta_flags(rx->sta, WLAN_STA_PS_DRIVER)) + ieee80211_sta_ps_deliver_poll_response(rx->sta); + else + set_sta_flags(rx->sta, WLAN_STA_PSPOLL); + + /* Free PS Poll skb here instead of returning RX_DROP that would + * count as an dropped frame. */ + dev_kfree_skb(rx->skb); + + return RX_QUEUED; + } else if (!ieee80211_has_morefrags(hdr->frame_control) && + !(status->rx_flags & IEEE80211_RX_DEFERRED_RELEASE) && + ieee80211_has_pm(hdr->frame_control) && + (ieee80211_is_data_qos(hdr->frame_control) || + ieee80211_is_qos_nullfunc(hdr->frame_control))) { + tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK; + ac = ieee802_1d_to_ac[tid & 7]; + + /* + * If this AC is not trigger-enabled do nothing. + * + * NB: This could/should check a separate bitmap of trigger- + * enabled queues, but for now we only implement uAPSD w/o + * TSPEC changes to the ACs, so they're always the same. + */ + if (!(rx->sta->sta.uapsd_queues & BIT(ac))) + return RX_CONTINUE; + + /* if we are in a service period, do nothing */ + if (test_sta_flags(rx->sta, WLAN_STA_SP)) + return RX_CONTINUE; + + if (!test_sta_flags(rx->sta, WLAN_STA_PS_DRIVER)) + ieee80211_sta_ps_deliver_uapsd(rx->sta); + else + set_sta_flags(rx->sta, WLAN_STA_UAPSD); + } + + return RX_CONTINUE; +} + static ieee80211_rx_result debug_noinline ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx) { @@ -1472,33 +1545,6 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx) return RX_CONTINUE; } -static ieee80211_rx_result debug_noinline -ieee80211_rx_h_ps_poll(struct ieee80211_rx_data *rx) -{ - struct ieee80211_sub_if_data *sdata = rx->sdata; - __le16 fc = ((struct ieee80211_hdr *)rx->skb->data)->frame_control; - struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb); - - if (likely(!rx->sta || !ieee80211_is_pspoll(fc) || - !(status->rx_flags & IEEE80211_RX_RA_MATCH))) - return RX_CONTINUE; - - if ((sdata->vif.type != NL80211_IFTYPE_AP) && - (sdata->vif.type != NL80211_IFTYPE_AP_VLAN)) - return RX_DROP_UNUSABLE; - - if (!test_sta_flags(rx->sta, WLAN_STA_PS_DRIVER)) - ieee80211_sta_ps_deliver_poll_response(rx->sta); - else - set_sta_flags(rx->sta, WLAN_STA_PSPOLL); - - /* Free PS Poll skb here instead of returning RX_DROP that would - * count as an dropped frame. */ - dev_kfree_skb(rx->skb); - - return RX_QUEUED; -} - static ieee80211_rx_result debug_noinline ieee80211_rx_h_remove_qos_control(struct ieee80211_rx_data *rx) { @@ -2567,9 +2613,9 @@ static void ieee80211_rx_handlers(struct ieee80211_rx_data *rx) CALL_RXH(ieee80211_rx_h_decrypt) CALL_RXH(ieee80211_rx_h_check_more_data) + CALL_RXH(ieee80211_rx_h_uapsd_and_pspoll) CALL_RXH(ieee80211_rx_h_sta_process) CALL_RXH(ieee80211_rx_h_defragment) - CALL_RXH(ieee80211_rx_h_ps_poll) CALL_RXH(ieee80211_rx_h_michael_mic_verify) /* must be after MMIC verify so header is counted in MPDU mic */ #ifdef CONFIG_MAC80211_MESH diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index b3f841948c09..f9079e478f77 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -248,6 +248,9 @@ static void sta_unblock(struct work_struct *wk) else if (test_and_clear_sta_flags(sta, WLAN_STA_PSPOLL)) { clear_sta_flags(sta, WLAN_STA_PS_DRIVER); ieee80211_sta_ps_deliver_poll_response(sta); + } else if (test_and_clear_sta_flags(sta, WLAN_STA_UAPSD)) { + clear_sta_flags(sta, WLAN_STA_PS_DRIVER); + ieee80211_sta_ps_deliver_uapsd(sta); } else clear_sta_flags(sta, WLAN_STA_PS_DRIVER); } @@ -1117,6 +1120,8 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta) struct sk_buff_head pending; int filtered = 0, buffered = 0, ac; + clear_sta_flags(sta, WLAN_STA_SP); + BUILD_BUG_ON(BITS_TO_LONGS(STA_TID_NUM) > 1); sta->driver_buffered_tids = 0; @@ -1152,32 +1157,28 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta) #endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */ } -void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta) +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; - struct sk_buff *skb = NULL; bool found = false; bool more_data = false; int ac; unsigned long driver_release_tids = 0; - u8 ignore_for_response = sta->sta.uapsd_queues; + struct sk_buff_head frames; - /* - * 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; + __skb_queue_head_init(&frames); /* - * Get response frame and more data bit for it. + * Get response frame(s) and more data bit for it. */ for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { unsigned long tids; - if (ignore_for_response & BIT(ac)) + if (ignored_acs & BIT(ac)) continue; tids = ieee80211_tids_for_ac(ac); @@ -1187,14 +1188,22 @@ void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta) if (driver_release_tids) { found = true; } else { - 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) + 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); + } } /* @@ -1202,7 +1211,8 @@ void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta) * certainly there's more data if we release just a * single frame now (from a single TID). */ - if (hweight16(driver_release_tids) > 1) { + if (reason == IEEE80211_FRAME_RELEASE_PSPOLL && + hweight16(driver_release_tids) > 1) { more_data = true; driver_release_tids = BIT(ffs(driver_release_tids) - 1); @@ -1225,38 +1235,56 @@ void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta) * Should we send it a null-func frame indicating we * have nothing buffered for it? */ - printk(KERN_DEBUG "%s: STA %pM sent PS Poll even " - "though there are no buffered frames for it\n", - sdata->name, sta->sta.addr); + if (reason == IEEE80211_FRAME_RELEASE_PSPOLL) + 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 */ return; } - if (skb) { - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - struct ieee80211_hdr *hdr = - (struct ieee80211_hdr *) skb->data; + if (!driver_release_tids) { + struct sk_buff_head pending; + struct sk_buff *skb; - /* - * 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_PSPOLL_RESPONSE; + skb_queue_head_init(&pending); -#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG - printk(KERN_DEBUG "STA %pM aid %d: PS Poll\n", - sta->sta.addr, sta->sta.aid); -#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */ + while ((skb = __skb_dequeue(&frames))) { + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_hdr *hdr = (void *) skb->data; - /* 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); + /* + * 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 (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; + info->flags |= IEEE80211_TX_STATUS_EOSP | + IEEE80211_TX_CTL_REQ_TX_STATUS; + } - ieee80211_add_pending_skb(local, skb); + __skb_queue_tail(&pending, skb); + } + + ieee80211_add_pending_skbs(local, &pending); sta_info_recalc_tim(sta); } else { @@ -1271,8 +1299,7 @@ void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta) * needs to be set anyway. */ drv_release_buffered_frames(local, sta, driver_release_tids, - 1, IEEE80211_FRAME_RELEASE_PSPOLL, - more_data); + n_frames, reason, more_data); /* * Note that we don't recalculate the TIM bit here as it would @@ -1285,6 +1312,59 @@ void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta) } } +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; + + /* Ohh, finally, the service period starts :-) */ + set_sta_flags(sta, WLAN_STA_SP); + + 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) { diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 8589afad3295..751ad25f925f 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -46,6 +46,11 @@ * @WLAN_STA_TDLS_PEER: Station is a TDLS peer. * @WLAN_STA_TDLS_PEER_AUTH: This TDLS peer is authorized to send direct * packets. This means the link is enabled. + * @WLAN_STA_UAPSD: Station requested unscheduled SP while driver was + * keeping station in power-save mode, reply when the driver + * unblocks the station. + * @WLAN_STA_SP: Station is in a service period, so don't try to + * reply to other uAPSD trigger frames. */ enum ieee80211_sta_info_flags { WLAN_STA_AUTH = 1<<0, @@ -63,6 +68,8 @@ enum ieee80211_sta_info_flags { WLAN_STA_PSPOLL = 1<<13, WLAN_STA_TDLS_PEER = 1<<15, WLAN_STA_TDLS_PEER_AUTH = 1<<16, + WLAN_STA_UAPSD = 1<<17, + WLAN_STA_SP = 1<<18, }; #define STA_TID_NUM 16 @@ -539,5 +546,6 @@ void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata, void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta); void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta); +void ieee80211_sta_ps_deliver_uapsd(struct sta_info *sta); #endif /* STA_INFO_H */ diff --git a/net/mac80211/status.c b/net/mac80211/status.c index 94475eb51d28..b5df9be4d043 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c @@ -76,8 +76,16 @@ static void ieee80211_handle_filtered_frame(struct ieee80211_local *local, hdr->frame_control &= ~cpu_to_le16(IEEE80211_FCTL_MOREDATA); if (ieee80211_is_data_qos(hdr->frame_control)) { - int tid = *ieee80211_get_qos_ctl(hdr) & - IEEE80211_QOS_CTL_TID_MASK; + u8 *p = ieee80211_get_qos_ctl(hdr); + int tid = *p & IEEE80211_QOS_CTL_TID_MASK; + + /* + * Clear EOSP if set, this could happen e.g. + * if an absence period (us being a P2P GO) + * shortens the SP. + */ + if (*p & IEEE80211_QOS_CTL_EOSP) + *p &= ~IEEE80211_QOS_CTL_EOSP; ac = ieee802_1d_to_ac[tid & 7]; } else { ac = IEEE80211_AC_BE; @@ -276,6 +284,9 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) if (memcmp(hdr->addr2, sta->sdata->vif.addr, ETH_ALEN)) continue; + if (info->flags & IEEE80211_TX_STATUS_EOSP) + clear_sta_flags(sta, WLAN_STA_SP); + acked = !!(info->flags & IEEE80211_TX_STAT_ACK); if (!acked && test_sta_flags(sta, WLAN_STA_PS_STA)) { /* diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index a1029449df44..a0676d39fe8f 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -456,7 +456,7 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx) staflags = get_sta_flags(sta); if (unlikely((staflags & (WLAN_STA_PS_STA | WLAN_STA_PS_DRIVER)) && - !(info->flags & IEEE80211_TX_CTL_PSPOLL_RESPONSE))) { + !(info->flags & IEEE80211_TX_CTL_POLL_RESPONSE))) { int ac = skb_get_queue_mapping(tx->skb); #ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG @@ -497,9 +497,9 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx) } #ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG else if (unlikely(staflags & WLAN_STA_PS_STA)) { - printk(KERN_DEBUG "%s: STA %pM in PS mode, but pspoll " - "set -> send frame\n", tx->sdata->name, - sta->sta.addr); + printk(KERN_DEBUG + "%s: STA %pM in PS mode, but polling/in SP -> send frame\n", + tx->sdata->name, sta->sta.addr); } #endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */ -- cgit v1.2.3 From deeaee197b0fa694ba6c8f02cdb57b3be7115b4f Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 29 Sep 2011 16:04:35 +0200 Subject: mac80211: reply only once to each PS-poll If a PS-poll frame is retried (but was received) there is no way to detect that since it has no sequence number. As a consequence, the standard asks us to not react to PS-poll frames until the response to one made it out (was ACKed or lost). Implement this by using the WLAN_STA_SP flags to also indicate a PS-Poll "service period" and the IEEE80211_TX_STATUS_EOSP flag for the response packet to indicate the end of the "SP" as usual. We could use separate flags, but that will most likely completely confuse drivers, and while the standard doesn't exclude simultaneously polling using uAPSD and PS-Poll, doing that seems quite problematic. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- include/net/mac80211.h | 9 ++++++++- net/mac80211/rx.c | 10 ++++++---- net/mac80211/sta_info.c | 22 +++++++++++----------- net/mac80211/sta_info.h | 2 +- 4 files changed, 26 insertions(+), 17 deletions(-) (limited to 'include/net') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index dd07964ef154..ee6449eff7dc 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -370,7 +370,8 @@ struct ieee80211_bss_conf { * @IEEE80211_TX_STATUS_EOSP: This packet marks the end of service period, * when its status is reported the service period ends. For frames in * an SP that mac80211 transmits, it is already set; for driver frames - * the driver may set this flag. + * the driver may set this flag. It is also used to do the same for + * PS-Poll responses. * * Note: If you have to add new flags to the enumeration, then don't * forget to update %IEEE80211_TX_TEMPORARY_FLAGS when necessary. @@ -1959,6 +1960,12 @@ enum ieee80211_frame_release_type { * more-data bit must always be set. * The @tids parameter tells the driver which TIDs to release frames * from, for PS-poll it will always have only a single bit set. + * In the case this is used for a PS-poll initiated release, the + * @num_frames parameter will always be 1 so code can be shared. In + * this case the driver must also set %IEEE80211_TX_STATUS_EOSP flag + * on the TX status (and must report TX status) so that the PS-poll + * period is properly ended. This is used to avoid sending multiple + * responses for a retried PS-poll frame. * In the case this is used for uAPSD, the @num_frames parameter may be * bigger than one, but the driver may send fewer frames (it must send * at least one, however). In this case it is also responsible for diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 9a703f00b5fb..32c8ee43f720 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -1194,10 +1194,12 @@ ieee80211_rx_h_uapsd_and_pspoll(struct ieee80211_rx_data *rx) return RX_CONTINUE; if (unlikely(ieee80211_is_pspoll(hdr->frame_control))) { - if (!test_sta_flags(rx->sta, WLAN_STA_PS_DRIVER)) - ieee80211_sta_ps_deliver_poll_response(rx->sta); - else - set_sta_flags(rx->sta, WLAN_STA_PSPOLL); + if (!test_sta_flags(rx->sta, WLAN_STA_SP)) { + if (!test_sta_flags(rx->sta, WLAN_STA_PS_DRIVER)) + ieee80211_sta_ps_deliver_poll_response(rx->sta); + else + set_sta_flags(rx->sta, WLAN_STA_PSPOLL); + } /* Free PS Poll skb here instead of returning RX_DROP that would * count as an dropped frame. */ diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index d9cb56f548a9..5732e4d0cc21 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -1217,13 +1217,12 @@ static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata, /* * Tell TX path to send this frame even though the * STA may still remain is PS mode after this frame - * exchange. + * exchange. Also set EOSP to indicate this packet + * ends the poll/service period. */ - info->flags |= IEEE80211_TX_CTL_POLL_RESPONSE; - - if (uapsd) - info->flags |= IEEE80211_TX_STATUS_EOSP | - IEEE80211_TX_CTL_REQ_TX_STATUS; + info->flags |= IEEE80211_TX_CTL_POLL_RESPONSE | + IEEE80211_TX_STATUS_EOSP | + IEEE80211_TX_CTL_REQ_TX_STATUS; ieee80211_xmit(sdata, skb); } @@ -1241,6 +1240,9 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta, unsigned long driver_release_tids = 0; struct sk_buff_head frames; + /* Service or PS-Poll period starts */ + set_sta_flags(sta, WLAN_STA_SP); + __skb_queue_head_init(&frames); /* @@ -1357,10 +1359,11 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta, /* set EOSP for the frame */ u8 *p = ieee80211_get_qos_ctl(hdr); *p |= IEEE80211_QOS_CTL_EOSP; - info->flags |= IEEE80211_TX_STATUS_EOSP | - IEEE80211_TX_CTL_REQ_TX_STATUS; } + info->flags |= IEEE80211_TX_STATUS_EOSP | + IEEE80211_TX_CTL_REQ_TX_STATUS; + __skb_queue_tail(&pending, skb); } @@ -1422,9 +1425,6 @@ void ieee80211_sta_ps_deliver_uapsd(struct sta_info *sta) if (!delivery_enabled) return; - /* Ohh, finally, the service period starts :-) */ - set_sta_flags(sta, WLAN_STA_SP); - switch (sta->sta.max_sp) { case 1: n_frames = 2; diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 751ad25f925f..348847a32630 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -50,7 +50,7 @@ * keeping station in power-save mode, reply when the driver * unblocks the station. * @WLAN_STA_SP: Station is in a service period, so don't try to - * reply to other uAPSD trigger frames. + * reply to other uAPSD trigger frames or PS-Poll. */ enum ieee80211_sta_info_flags { WLAN_STA_AUTH = 1<<0, -- cgit v1.2.3 From 40b96408831f038b1a6b45e8b22cd050f82a3896 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 29 Sep 2011 16:04:38 +0200 Subject: 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: Johannes Berg Signed-off-by: John W. Linville --- include/net/mac80211.h | 17 +++++++++++++++++ net/mac80211/driver-ops.h | 15 +++++++++++++++ net/mac80211/driver-trace.h | 22 +++++++++++++++++++++- net/mac80211/sta_info.c | 34 +++++++++++++++++++++++++--------- 4 files changed, 78 insertions(+), 10 deletions(-) (limited to 'include/net') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index ee6449eff7dc..778572d38bd3 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -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, diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 8fa0d2edf54c..68721d379fe1 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -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 */ diff --git a/net/mac80211/driver-trace.h b/net/mac80211/driver-trace.h index 531fbd086794..aef08969e353 100644 --- a/net/mac80211/driver-trace.h +++ b/net/mac80211/driver-trace.h @@ -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. */ diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index a00358224cd5..907b42081f3c 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -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); -- cgit v1.2.3 From 37fbd9080088f5f98ab81a6f2ad456857971a089 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 29 Sep 2011 16:04:39 +0200 Subject: mac80211: allow out-of-band EOSP notification 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. This is needed in the case of a P2P GO going into an absence period without transmitting any frames that should be driver-released as in this case there's no other way to inform mac80211 that the service period ended. Note that for drivers that don't use the _irqsafe functions another version of this function will be required. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- include/net/mac80211.h | 24 ++++++++++++++++++++++-- net/mac80211/driver-trace.h | 22 ++++++++++++++++++++++ net/mac80211/ieee80211_i.h | 5 +++++ net/mac80211/main.c | 14 ++++++++++++++ net/mac80211/sta_info.c | 25 +++++++++++++++++++++++++ 5 files changed, 88 insertions(+), 2 deletions(-) (limited to 'include/net') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 778572d38bd3..3df32a04402c 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1971,7 +1971,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. * @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 @@ -1981,7 +1982,8 @@ enum ieee80211_frame_release_type { * 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. + * bit in the QoS header correctly. Alternatively, it can also call the + * ieee80211_sta_eosp_irqsafe() function. * 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. @@ -3112,6 +3114,24 @@ struct ieee80211_sta *ieee80211_find_sta_by_ifaddr(struct ieee80211_hw *hw, void ieee80211_sta_block_awake(struct ieee80211_hw *hw, 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 + * mac80211 in the TX status about the EOSP, it must clear the + * %IEEE80211_TX_STATUS_EOSP bit and call this function instead. + * This applies for PS-Poll as well as uAPSD. + * + * 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! If you need + * the non-irqsafe version of this, you need to add it. + */ +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() diff --git a/net/mac80211/driver-trace.h b/net/mac80211/driver-trace.h index aef08969e353..2af4fca55337 100644 --- a/net/mac80211/driver-trace.h +++ b/net/mac80211/driver-trace.h @@ -1498,6 +1498,28 @@ TRACE_EVENT(api_enable_rssi_reports, ) ); +TRACE_EVENT(api_eosp, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sta *sta), + + TP_ARGS(local, sta), + + TP_STRUCT__entry( + LOCAL_ENTRY + STA_ENTRY + ), + + TP_fast_assign( + LOCAL_ASSIGN; + STA_ASSIGN; + ), + + TP_printk( + LOCAL_PR_FMT STA_PR_FMT, + LOCAL_PR_ARG, STA_PR_FMT + ) +); + /* * Tracing for internal functions * (which may also be called in response to driver calls) diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index da3206450192..9fa5f8a674bc 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -664,6 +664,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 { diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 336ceb9d2462..17b038aeac9b 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -325,6 +325,8 @@ u32 ieee80211_reset_erp_info(struct ieee80211_sub_if_data *sdata) 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,18 @@ static void ieee80211_tasklet_handler(unsigned long data) 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_flag(sta, WLAN_STA_SP); + break; + } + dev_kfree_skb(skb); + break; default: WARN(1, "mac80211: Packet is of unknown type %d\n", skb->pkt_type); diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 907b42081f3c..076593bffbcf 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -1478,6 +1478,31 @@ void ieee80211_sta_block_awake(struct ieee80211_hw *hw, } 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; + + 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) { -- cgit v1.2.3 From 4b801bc969364a980c1366e48155d1a29d20661b Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 29 Sep 2011 16:04:40 +0200 Subject: mac80211: document client powersave With the addition of uAPSD and driver buffering the powersave handling has gotten quite complex. Add a section to the documentation to explain it for anyone wanting to implement it. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- Documentation/DocBook/80211.tmpl | 11 ++++- include/net/mac80211.h | 89 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+), 1 deletion(-) (limited to 'include/net') diff --git a/Documentation/DocBook/80211.tmpl b/Documentation/DocBook/80211.tmpl index 445289cd0e65..2014155c899d 100644 --- a/Documentation/DocBook/80211.tmpl +++ b/Documentation/DocBook/80211.tmpl @@ -433,8 +433,18 @@ Insert notes about VLAN interfaces with hw crypto here or in the hw crypto chapter. +
+ support for powersaving clients +!Pinclude/net/mac80211.h AP support for powersaving clients +
!Finclude/net/mac80211.h ieee80211_get_buffered_bc !Finclude/net/mac80211.h ieee80211_beacon_get +!Finclude/net/mac80211.h ieee80211_sta_eosp_irqsafe +!Finclude/net/mac80211.h ieee80211_frame_release_type +!Finclude/net/mac80211.h ieee80211_sta_ps_transition +!Finclude/net/mac80211.h ieee80211_sta_ps_transition_ni +!Finclude/net/mac80211.h ieee80211_sta_set_buffered +!Finclude/net/mac80211.h ieee80211_sta_block_awake @@ -460,7 +470,6 @@ !Finclude/net/mac80211.h sta_notify_cmd !Finclude/net/mac80211.h ieee80211_find_sta !Finclude/net/mac80211.h ieee80211_find_sta_by_ifaddr -!Finclude/net/mac80211.h ieee80211_sta_block_awake diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 3df32a04402c..bc799304be71 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1538,6 +1538,95 @@ ieee80211_get_alt_retry_rate(const struct ieee80211_hw *hw, * This rule applies to all other FIF flags as well. */ +/** + * DOC: AP support for powersaving clients + * + * In order to implement AP and P2P GO modes, mac80211 has support for + * client powersaving, both "legacy" PS (PS-Poll/null data) and uAPSD. + * There currently is no support for sAPSD. + * + * There is one assumption that mac80211 makes, namely that a client + * will not poll with PS-Poll and trigger with uAPSD at the same time. + * Both are supported, and both can be used by the same client, but + * they can't be used concurrently by the same client. This simplifies + * the driver code. + * + * The first thing to keep in mind is that there is a flag for complete + * driver implementation: %IEEE80211_HW_AP_LINK_PS. If this flag is set, + * mac80211 expects the driver to handle most of the state machine for + * powersaving clients and will ignore the PM bit in incoming frames. + * Drivers then use ieee80211_sta_ps_transition() to inform mac80211 of + * stations' powersave transitions. In this mode, mac80211 also doesn't + * handle PS-Poll/uAPSD. + * + * In the mode without %IEEE80211_HW_AP_LINK_PS, mac80211 will check the + * PM bit in incoming frames for client powersave transitions. When a + * station goes to sleep, we will stop transmitting to it. There is, + * however, a race condition: a station might go to sleep while there is + * data buffered on hardware queues. If the device has support for this + * it will reject frames, and the driver should give the frames back to + * mac80211 with the %IEEE80211_TX_STAT_TX_FILTERED flag set which will + * cause mac80211 to retry the frame when the station wakes up. The + * driver is also notified of powersave transitions by calling its + * @sta_notify callback. + * + * When the station is asleep, it has three choices: it can wake up, + * it can PS-Poll, or it can possibly start a uAPSD service period. + * Waking up is implemented by simply transmitting all buffered (and + * filtered) frames to the station. This is the easiest case. When + * the station sends a PS-Poll or a uAPSD trigger frame, mac80211 + * will inform the driver of this with the @allow_buffered_frames + * callback; this callback is optional. mac80211 will then transmit + * the frames as usual and set the %IEEE80211_TX_CTL_POLL_RESPONSE + * on each frame. The last frame in the service period (or the only + * response to a PS-Poll) also has %IEEE80211_TX_STATUS_EOSP set to + * indicate that it ends the service period; as this frame must have + * TX status report it also sets %IEEE80211_TX_CTL_REQ_TX_STATUS. + * When TX status is reported for this frame, the service period is + * marked has having ended and a new one can be started by the peer. + * + * Another race condition can happen on some devices like iwlwifi + * when there are frames queued for the station and it wakes up + * or polls; the frames that are already queued could end up being + * transmitted first instead, causing reordering and/or wrong + * processing of the EOSP. The cause is that allowing frames to be + * transmitted to a certain station is out-of-band communication to + * the device. To allow this problem to be solved, the driver can + * call ieee80211_sta_block_awake() if frames are buffered when it + * is notified that the station went to sleep. When all these frames + * have been filtered (see above), it must call the function again + * to indicate that the station is no longer blocked. + * + * If the driver buffers frames in the driver for aggregation in any + * way, it must use the ieee80211_sta_set_buffered() call when it is + * notified of the station going to sleep to inform mac80211 of any + * TIDs that have frames buffered. Note that when a station wakes up + * this information is reset (hence the requirement to call it when + * informed of the station going to sleep). Then, when a service + * period starts for any reason, @release_buffered_frames is called + * with the number of frames to be released and which TIDs they are + * to come from. In this case, the driver is responsible for setting + * the EOSP (for uAPSD) and MORE_DATA bits in the released frames, + * to help the @more_data paramter is passed to tell the driver if + * there is more data on other TIDs -- the TIDs to release frames + * from are ignored since mac80211 doesn't know how many frames the + * buffers for those TIDs contain. + * + * If the driver also implement GO mode, where absence periods may + * shorten service periods (or abort PS-Poll responses), it must + * filter those response frames except in the case of frames that + * are buffered in the driver -- those must remain buffered to avoid + * reordering. Because it is possible that no frames are released + * in this case, the driver must call ieee80211_sta_eosp_irqsafe() + * to indicate to mac80211 that the service period ended anyway. + * + * Finally, if frames from multiple TIDs are released from mac80211 + * but the driver might reorder them, it must clear & set the flags + * appropriately (only the last frame may have %IEEE80211_TX_STATUS_EOSP) + * and also take care of the EOSP and MORE_DATA bits in the frame. + * The driver may also use ieee80211_sta_eosp_irqsafe() in this case. + */ + /** * enum ieee80211_filter_flags - hardware filter flags * -- cgit v1.2.3 From 893d73f4a15bda966cb72f84897898eb235e134c Mon Sep 17 00:00:00 2001 From: Helmut Schaa Date: Thu, 29 Sep 2011 13:42:25 +0200 Subject: mac80211: Allow noack flag overwrite for injected frames Allow injected unicast frames to be sent without having to wait for an ACK. Signed-off-by: Helmut Schaa Signed-off-by: John W. Linville --- include/net/ieee80211_radiotap.h | 1 + net/mac80211/tx.c | 15 +++++++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) (limited to 'include/net') diff --git a/include/net/ieee80211_radiotap.h b/include/net/ieee80211_radiotap.h index b0be5fb9de19..7e2c4d483ad0 100644 --- a/include/net/ieee80211_radiotap.h +++ b/include/net/ieee80211_radiotap.h @@ -251,6 +251,7 @@ enum ieee80211_radiotap_type { * retries */ #define IEEE80211_RADIOTAP_F_TX_CTS 0x0002 /* used cts 'protection' */ #define IEEE80211_RADIOTAP_F_TX_RTS 0x0004 /* used rts/cts handshake */ +#define IEEE80211_RADIOTAP_F_TX_NOACK 0x0008 /* don't expect an ack */ /* For IEEE80211_RADIOTAP_MCS */ diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index ae5dd85f1e93..ad2ee4a90ec4 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1054,6 +1054,7 @@ static bool __ieee80211_parse_tx_radiotap(struct ieee80211_tx_data *tx, struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); int ret = ieee80211_radiotap_iterator_init(&iterator, rthdr, skb->len, NULL); + u16 txflags; info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; tx->flags &= ~IEEE80211_TX_FRAGMENTED; @@ -1102,6 +1103,13 @@ static bool __ieee80211_parse_tx_radiotap(struct ieee80211_tx_data *tx, tx->flags |= IEEE80211_TX_FRAGMENTED; break; + case IEEE80211_RADIOTAP_TX_FLAGS: + txflags = le16_to_cpu(get_unaligned((__le16*) + iterator.this_arg)); + if (txflags & IEEE80211_RADIOTAP_F_TX_NOACK) + info->flags |= IEEE80211_TX_CTL_NO_ACK; + break; + /* * Please update the file * Documentation/networking/mac80211-injection.txt @@ -1266,8 +1274,11 @@ ieee80211_tx_prepare(struct ieee80211_sub_if_data *sdata, tx->flags |= IEEE80211_TX_UNICAST; if (unlikely(local->wifi_wme_noack_test)) info->flags |= IEEE80211_TX_CTL_NO_ACK; - else - info->flags &= ~IEEE80211_TX_CTL_NO_ACK; + /* + * Flags are initialized to 0. Hence, no need to + * explicitly unset IEEE80211_TX_CTL_NO_ACK since + * it might already be set for injected frames. + */ } if (tx->flags & IEEE80211_TX_FRAGMENTED) { -- cgit v1.2.3 From b6f35301efda5e94342cfcca9e29b7b3e9a5f827 Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Thu, 29 Sep 2011 20:34:04 +0530 Subject: mac80211: Send nullfunc frames at lower rate during connection monitor Recently mac80211 was changed to use nullfunc instead of probe request for connection monitoring for tx ack status reporting hardwares. Sometimes in congested network, STA got disconnected quickly after the association. It was observered that the rate control was not adopted to environment due to minimal transmission. As the nullfunc are used for monitoring purpose, these frames should not be sacrificed for rate control updation. So it is better to send the monitoring null func frames at minimum rate that could help to retain the connection. Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- include/net/mac80211.h | 4 ++++ net/mac80211/mlme.c | 5 +++++ net/mac80211/rate.c | 8 +++++--- 3 files changed, 14 insertions(+), 3 deletions(-) (limited to 'include/net') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index bc799304be71..135e897b61c7 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -372,6 +372,9 @@ struct ieee80211_bss_conf { * an SP that mac80211 transmits, it is already set; for driver frames * the driver may set this flag. It is also used to do the same for * PS-Poll responses. + * @IEEE80211_TX_CTL_USE_MINRATE: This frame will be sent at lowest rate. + * This flag is used to send nullfunc frame at minimum rate when + * the nullfunc is used for connection monitoring purpose. * * Note: If you have to add new flags to the enumeration, then don't * forget to update %IEEE80211_TX_TEMPORARY_FLAGS when necessary. @@ -404,6 +407,7 @@ enum mac80211_tx_control_flags { IEEE80211_TX_INTFL_TKIP_MIC_FAILURE = BIT(26), IEEE80211_TX_CTL_NO_CCK_RATE = BIT(27), IEEE80211_TX_STATUS_EOSP = BIT(28), + IEEE80211_TX_CTL_USE_MINRATE = BIT(29), }; #define IEEE80211_TX_CTL_STBC_SHIFT 23 diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index c4e8901c96f6..0e5d8daba1ee 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -348,6 +348,7 @@ void ieee80211_send_nullfunc(struct ieee80211_local *local, { struct sk_buff *skb; struct ieee80211_hdr_3addr *nullfunc; + struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; skb = ieee80211_nullfunc_get(&local->hw, &sdata->vif); if (!skb) @@ -358,6 +359,10 @@ void ieee80211_send_nullfunc(struct ieee80211_local *local, nullfunc->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM); IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; + if (ifmgd->flags & (IEEE80211_STA_BEACON_POLL | + IEEE80211_STA_CONNECTION_POLL)) + IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_USE_MINRATE; + ieee80211_tx_skb(sdata, skb); } diff --git a/net/mac80211/rate.c b/net/mac80211/rate.c index f61244c0e0a2..ff5c3aa48a15 100644 --- a/net/mac80211/rate.c +++ b/net/mac80211/rate.c @@ -199,7 +199,7 @@ static void rate_control_release(struct kref *kref) kfree(ctrl_ref); } -static bool rc_no_data_or_no_ack(struct ieee80211_tx_rate_control *txrc) +static bool rc_no_data_or_no_ack_use_min(struct ieee80211_tx_rate_control *txrc) { struct sk_buff *skb = txrc->skb; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; @@ -208,7 +208,9 @@ static bool rc_no_data_or_no_ack(struct ieee80211_tx_rate_control *txrc) fc = hdr->frame_control; - return (info->flags & IEEE80211_TX_CTL_NO_ACK) || !ieee80211_is_data(fc); + return (info->flags & (IEEE80211_TX_CTL_NO_ACK | + IEEE80211_TX_CTL_USE_MINRATE)) || + !ieee80211_is_data(fc); } static void rc_send_low_broadcast(s8 *idx, u32 basic_rates, @@ -262,7 +264,7 @@ bool rate_control_send_low(struct ieee80211_sta *sta, struct ieee80211_supported_band *sband = txrc->sband; int mcast_rate; - if (!sta || !priv_sta || rc_no_data_or_no_ack(txrc)) { + if (!sta || !priv_sta || rc_no_data_or_no_ack_use_min(txrc)) { if ((sband->band != IEEE80211_BAND_2GHZ) || !(info->flags & IEEE80211_TX_CTL_NO_CCK_RATE)) info->control.rates[0].idx = -- cgit v1.2.3 From 8a3a3c85e44d58f5af0adac74a0b866ba89a1978 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Sun, 2 Oct 2011 10:15:52 +0200 Subject: mac80211: pass vif param to conf_tx() callback tx params should be configured per interface. add ieee80211_vif param to the conf_tx callback, and change all the drivers that use this callback. The following spatch was used: @rule1@ struct ieee80211_ops ops; identifier conf_tx_op; @@ ops.conf_tx = conf_tx_op; @rule2@ identifier rule1.conf_tx_op; identifier hw, queue, params; @@ conf_tx_op ( - struct ieee80211_hw *hw, + struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 queue, const struct ieee80211_tx_queue_params *params) {...} Signed-off-by: Eliad Peller Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath5k/mac80211-ops.c | 2 +- drivers/net/wireless/ath/ath9k/htc_drv_main.c | 3 ++- drivers/net/wireless/ath/ath9k/main.c | 3 ++- drivers/net/wireless/ath/carl9170/main.c | 3 ++- drivers/net/wireless/b43/main.c | 3 ++- drivers/net/wireless/b43legacy/main.c | 3 ++- drivers/net/wireless/iwlegacy/iwl-core.c | 3 ++- drivers/net/wireless/iwlegacy/iwl-core.h | 3 ++- drivers/net/wireless/iwlwifi/iwl-core.c | 5 +++-- drivers/net/wireless/iwlwifi/iwl-core.h | 3 ++- drivers/net/wireless/mac80211_hwsim.c | 3 ++- drivers/net/wireless/mwl8k.c | 5 +++-- drivers/net/wireless/p54/main.c | 3 ++- drivers/net/wireless/rt2x00/rt2400pci.c | 5 +++-- drivers/net/wireless/rt2x00/rt2800lib.c | 5 +++-- drivers/net/wireless/rt2x00/rt2800lib.h | 3 ++- drivers/net/wireless/rt2x00/rt2x00.h | 3 ++- drivers/net/wireless/rt2x00/rt2x00mac.c | 3 ++- drivers/net/wireless/rt2x00/rt61pci.c | 5 +++-- drivers/net/wireless/rt2x00/rt73usb.c | 5 +++-- drivers/net/wireless/rtl818x/rtl8187/dev.c | 3 ++- drivers/net/wireless/rtlwifi/core.c | 3 ++- drivers/net/wireless/wl1251/main.c | 3 ++- drivers/net/wireless/wl12xx/main.c | 3 ++- drivers/staging/brcm80211/brcmsmac/mac80211_if.c | 7 ++++--- include/net/mac80211.h | 3 ++- net/mac80211/driver-ops.h | 3 ++- 27 files changed, 61 insertions(+), 35 deletions(-) (limited to 'include/net') diff --git a/drivers/net/wireless/ath/ath5k/mac80211-ops.c b/drivers/net/wireless/ath/ath5k/mac80211-ops.c index bba4f6fcf7e2..6ed4c0717e3e 100644 --- a/drivers/net/wireless/ath/ath5k/mac80211-ops.c +++ b/drivers/net/wireless/ath/ath5k/mac80211-ops.c @@ -563,7 +563,7 @@ ath5k_get_stats(struct ieee80211_hw *hw, static int -ath5k_conf_tx(struct ieee80211_hw *hw, u16 queue, +ath5k_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 queue, const struct ieee80211_tx_queue_params *params) { struct ath5k_hw *ah = hw->priv; diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c index 17dbbd9d2f53..0b9a0e8a4958 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c @@ -1352,7 +1352,8 @@ static int ath9k_htc_sta_remove(struct ieee80211_hw *hw, return ret; } -static int ath9k_htc_conf_tx(struct ieee80211_hw *hw, u16 queue, +static int ath9k_htc_conf_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u16 queue, const struct ieee80211_tx_queue_params *params) { struct ath9k_htc_priv *priv = hw->priv; diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 0ebf7321df12..988318665758 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -1842,7 +1842,8 @@ static void ath9k_sta_notify(struct ieee80211_hw *hw, } } -static int ath9k_conf_tx(struct ieee80211_hw *hw, u16 queue, +static int ath9k_conf_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u16 queue, const struct ieee80211_tx_queue_params *params) { struct ath_softc *sc = hw->priv; diff --git a/drivers/net/wireless/ath/carl9170/main.c b/drivers/net/wireless/ath/carl9170/main.c index 8b780d6d470f..beca71073e9b 100644 --- a/drivers/net/wireless/ath/carl9170/main.c +++ b/drivers/net/wireless/ath/carl9170/main.c @@ -1305,7 +1305,8 @@ static int carl9170_op_sta_remove(struct ieee80211_hw *hw, return 0; } -static int carl9170_op_conf_tx(struct ieee80211_hw *hw, u16 queue, +static int carl9170_op_conf_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u16 queue, const struct ieee80211_tx_queue_params *param) { struct ar9170 *ar = hw->priv; diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index 43400fb62e1c..7cf4125a1624 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -3559,7 +3559,8 @@ static void b43_qos_init(struct b43_wldev *dev) b43dbg(dev->wl, "QoS enabled\n"); } -static int b43_op_conf_tx(struct ieee80211_hw *hw, u16 _queue, +static int b43_op_conf_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u16 _queue, const struct ieee80211_tx_queue_params *params) { struct b43_wl *wl = hw_to_b43_wl(hw); diff --git a/drivers/net/wireless/b43legacy/main.c b/drivers/net/wireless/b43legacy/main.c index 468d1836548e..a3b72cd72c66 100644 --- a/drivers/net/wireless/b43legacy/main.c +++ b/drivers/net/wireless/b43legacy/main.c @@ -2466,7 +2466,8 @@ out: } } -static int b43legacy_op_conf_tx(struct ieee80211_hw *hw, u16 queue, +static int b43legacy_op_conf_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u16 queue, const struct ieee80211_tx_queue_params *params) { return 0; diff --git a/drivers/net/wireless/iwlegacy/iwl-core.c b/drivers/net/wireless/iwlegacy/iwl-core.c index 8928d47432df..2bd5659310d7 100644 --- a/drivers/net/wireless/iwlegacy/iwl-core.c +++ b/drivers/net/wireless/iwlegacy/iwl-core.c @@ -1250,7 +1250,8 @@ void iwl_legacy_clear_isr_stats(struct iwl_priv *priv) memset(&priv->isr_stats, 0, sizeof(priv->isr_stats)); } -int iwl_legacy_mac_conf_tx(struct ieee80211_hw *hw, u16 queue, +int iwl_legacy_mac_conf_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u16 queue, const struct ieee80211_tx_queue_params *params) { struct iwl_priv *priv = hw->priv; diff --git a/drivers/net/wireless/iwlegacy/iwl-core.h b/drivers/net/wireless/iwlegacy/iwl-core.h index b2df01c8f8f5..d1271fe07d4b 100644 --- a/drivers/net/wireless/iwlegacy/iwl-core.h +++ b/drivers/net/wireless/iwlegacy/iwl-core.h @@ -286,7 +286,8 @@ struct iwl_cfg { ***************************/ struct ieee80211_hw *iwl_legacy_alloc_all(struct iwl_cfg *cfg); -int iwl_legacy_mac_conf_tx(struct ieee80211_hw *hw, u16 queue, +int iwl_legacy_mac_conf_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u16 queue, const struct ieee80211_tx_queue_params *params); int iwl_legacy_mac_tx_last_beacon(struct ieee80211_hw *hw); void iwl_legacy_set_rxon_hwcrypto(struct iwl_priv *priv, diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c index fc400bb2bdff..0725603dbf1d 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.c +++ b/drivers/net/wireless/iwlwifi/iwl-core.c @@ -1123,8 +1123,9 @@ int iwl_send_statistics_request(struct iwl_priv *priv, u8 flags, bool clear) &statistics_cmd); } -int iwl_mac_conf_tx(struct ieee80211_hw *hw, u16 queue, - const struct ieee80211_tx_queue_params *params) +int iwl_mac_conf_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u16 queue, + const struct ieee80211_tx_queue_params *params) { struct iwl_priv *priv = hw->priv; struct iwl_rxon_context *ctx; diff --git a/drivers/net/wireless/iwlwifi/iwl-core.h b/drivers/net/wireless/iwlwifi/iwl-core.h index e55ffad83950..db50b650756c 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.h +++ b/drivers/net/wireless/iwlwifi/iwl-core.h @@ -236,7 +236,8 @@ struct iwl_cfg { * L i b * ***************************/ -int iwl_mac_conf_tx(struct ieee80211_hw *hw, u16 queue, +int iwl_mac_conf_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u16 queue, const struct ieee80211_tx_queue_params *params); int iwl_mac_tx_last_beacon(struct ieee80211_hw *hw); void iwl_set_rxon_hwcrypto(struct iwl_priv *priv, struct iwl_rxon_context *ctx, diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 34b79fc91e39..68455a2307cb 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -970,7 +970,8 @@ static int mac80211_hwsim_set_tim(struct ieee80211_hw *hw, } static int mac80211_hwsim_conf_tx( - struct ieee80211_hw *hw, u16 queue, + struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u16 queue, const struct ieee80211_tx_queue_params *params) { wiphy_debug(hw->wiphy, diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index ea1395aafa39..995695c28d5c 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -4915,7 +4915,8 @@ static int mwl8k_sta_add(struct ieee80211_hw *hw, return ret; } -static int mwl8k_conf_tx(struct ieee80211_hw *hw, u16 queue, +static int mwl8k_conf_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u16 queue, const struct ieee80211_tx_queue_params *params) { struct mwl8k_priv *priv = hw->priv; @@ -5462,7 +5463,7 @@ static int mwl8k_reload_firmware(struct ieee80211_hw *hw, char *fw_image) goto fail; for (i = 0; i < MWL8K_TX_WMM_QUEUES; i++) { - rc = mwl8k_conf_tx(hw, i, &priv->wmm_params[i]); + rc = mwl8k_conf_tx(hw, NULL, i, &priv->wmm_params[i]); if (rc) goto fail; } diff --git a/drivers/net/wireless/p54/main.c b/drivers/net/wireless/p54/main.c index 726a9343f514..ad9ae04d07aa 100644 --- a/drivers/net/wireless/p54/main.c +++ b/drivers/net/wireless/p54/main.c @@ -404,7 +404,8 @@ static void p54_configure_filter(struct ieee80211_hw *dev, p54_set_groupfilter(priv); } -static int p54_conf_tx(struct ieee80211_hw *dev, u16 queue, +static int p54_conf_tx(struct ieee80211_hw *dev, + struct ieee80211_vif *vif, u16 queue, const struct ieee80211_tx_queue_params *params) { struct p54_common *priv = dev->priv; diff --git a/drivers/net/wireless/rt2x00/rt2400pci.c b/drivers/net/wireless/rt2x00/rt2400pci.c index 7e9272b8f01d..3a6b40239bc1 100644 --- a/drivers/net/wireless/rt2x00/rt2400pci.c +++ b/drivers/net/wireless/rt2x00/rt2400pci.c @@ -1648,7 +1648,8 @@ static int rt2400pci_probe_hw(struct rt2x00_dev *rt2x00dev) /* * IEEE80211 stack callback functions. */ -static int rt2400pci_conf_tx(struct ieee80211_hw *hw, u16 queue, +static int rt2400pci_conf_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u16 queue, const struct ieee80211_tx_queue_params *params) { struct rt2x00_dev *rt2x00dev = hw->priv; @@ -1661,7 +1662,7 @@ static int rt2400pci_conf_tx(struct ieee80211_hw *hw, u16 queue, if (queue != 0) return -EINVAL; - if (rt2x00mac_conf_tx(hw, queue, params)) + if (rt2x00mac_conf_tx(hw, vif, queue, params)) return -EINVAL; /* diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c index 9688dd0a7ebd..3f183a15186e 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/rt2x00/rt2800lib.c @@ -4398,7 +4398,8 @@ int rt2800_set_rts_threshold(struct ieee80211_hw *hw, u32 value) } EXPORT_SYMBOL_GPL(rt2800_set_rts_threshold); -int rt2800_conf_tx(struct ieee80211_hw *hw, u16 queue_idx, +int rt2800_conf_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u16 queue_idx, const struct ieee80211_tx_queue_params *params) { struct rt2x00_dev *rt2x00dev = hw->priv; @@ -4414,7 +4415,7 @@ int rt2800_conf_tx(struct ieee80211_hw *hw, u16 queue_idx, * we are free to update the registers based on the value * in the queue parameter. */ - retval = rt2x00mac_conf_tx(hw, queue_idx, params); + retval = rt2x00mac_conf_tx(hw, vif, queue_idx, params); if (retval) return retval; diff --git a/drivers/net/wireless/rt2x00/rt2800lib.h b/drivers/net/wireless/rt2x00/rt2800lib.h index 6de128e9c612..8c3c281904fe 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.h +++ b/drivers/net/wireless/rt2x00/rt2800lib.h @@ -197,7 +197,8 @@ int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev); void rt2800_get_tkip_seq(struct ieee80211_hw *hw, u8 hw_key_idx, u32 *iv32, u16 *iv16); int rt2800_set_rts_threshold(struct ieee80211_hw *hw, u32 value); -int rt2800_conf_tx(struct ieee80211_hw *hw, u16 queue_idx, +int rt2800_conf_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u16 queue_idx, const struct ieee80211_tx_queue_params *params); u64 rt2800_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif); int rt2800_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h index cbf8eb334e96..2ec5c00235e6 100644 --- a/drivers/net/wireless/rt2x00/rt2x00.h +++ b/drivers/net/wireless/rt2x00/rt2x00.h @@ -1299,7 +1299,8 @@ void rt2x00mac_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *bss_conf, u32 changes); -int rt2x00mac_conf_tx(struct ieee80211_hw *hw, u16 queue, +int rt2x00mac_conf_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u16 queue, const struct ieee80211_tx_queue_params *params); void rt2x00mac_rfkill_poll(struct ieee80211_hw *hw); void rt2x00mac_flush(struct ieee80211_hw *hw, bool drop); diff --git a/drivers/net/wireless/rt2x00/rt2x00mac.c b/drivers/net/wireless/rt2x00/rt2x00mac.c index cef1c878c37e..bf0acff07807 100644 --- a/drivers/net/wireless/rt2x00/rt2x00mac.c +++ b/drivers/net/wireless/rt2x00/rt2x00mac.c @@ -713,7 +713,8 @@ void rt2x00mac_bss_info_changed(struct ieee80211_hw *hw, } EXPORT_SYMBOL_GPL(rt2x00mac_bss_info_changed); -int rt2x00mac_conf_tx(struct ieee80211_hw *hw, u16 queue_idx, +int rt2x00mac_conf_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u16 queue_idx, const struct ieee80211_tx_queue_params *params) { struct rt2x00_dev *rt2x00dev = hw->priv; diff --git a/drivers/net/wireless/rt2x00/rt61pci.c b/drivers/net/wireless/rt2x00/rt61pci.c index 6b6a8e2dcddc..bf55b4a311e3 100644 --- a/drivers/net/wireless/rt2x00/rt61pci.c +++ b/drivers/net/wireless/rt2x00/rt61pci.c @@ -2883,7 +2883,8 @@ static int rt61pci_probe_hw(struct rt2x00_dev *rt2x00dev) /* * IEEE80211 stack callback functions. */ -static int rt61pci_conf_tx(struct ieee80211_hw *hw, u16 queue_idx, +static int rt61pci_conf_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u16 queue_idx, const struct ieee80211_tx_queue_params *params) { struct rt2x00_dev *rt2x00dev = hw->priv; @@ -2899,7 +2900,7 @@ static int rt61pci_conf_tx(struct ieee80211_hw *hw, u16 queue_idx, * we are free to update the registers based on the value * in the queue parameter. */ - retval = rt2x00mac_conf_tx(hw, queue_idx, params); + retval = rt2x00mac_conf_tx(hw, vif, queue_idx, params); if (retval) return retval; diff --git a/drivers/net/wireless/rt2x00/rt73usb.c b/drivers/net/wireless/rt2x00/rt73usb.c index 6f51e39f5595..cfb19dbb0a67 100644 --- a/drivers/net/wireless/rt2x00/rt73usb.c +++ b/drivers/net/wireless/rt2x00/rt73usb.c @@ -2222,7 +2222,8 @@ static int rt73usb_probe_hw(struct rt2x00_dev *rt2x00dev) /* * IEEE80211 stack callback functions. */ -static int rt73usb_conf_tx(struct ieee80211_hw *hw, u16 queue_idx, +static int rt73usb_conf_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u16 queue_idx, const struct ieee80211_tx_queue_params *params) { struct rt2x00_dev *rt2x00dev = hw->priv; @@ -2238,7 +2239,7 @@ static int rt73usb_conf_tx(struct ieee80211_hw *hw, u16 queue_idx, * we are free to update the registers based on the value * in the queue parameter. */ - retval = rt2x00mac_conf_tx(hw, queue_idx, params); + retval = rt2x00mac_conf_tx(hw, vif, queue_idx, params); if (retval) return retval; diff --git a/drivers/net/wireless/rtl818x/rtl8187/dev.c b/drivers/net/wireless/rtl818x/rtl8187/dev.c index f5afa155ce91..24873b55b55c 100644 --- a/drivers/net/wireless/rtl818x/rtl8187/dev.c +++ b/drivers/net/wireless/rtl818x/rtl8187/dev.c @@ -1241,7 +1241,8 @@ static void rtl8187_configure_filter(struct ieee80211_hw *dev, rtl818x_iowrite32_async(priv, &priv->map->RX_CONF, priv->rx_conf); } -static int rtl8187_conf_tx(struct ieee80211_hw *dev, u16 queue, +static int rtl8187_conf_tx(struct ieee80211_hw *dev, + struct ieee80211_vif *vif, u16 queue, const struct ieee80211_tx_queue_params *params) { struct rtl8187_priv *priv = dev->priv; diff --git a/drivers/net/wireless/rtlwifi/core.c b/drivers/net/wireless/rtlwifi/core.c index 21e40f62a8d7..3f0f056fae9c 100644 --- a/drivers/net/wireless/rtlwifi/core.c +++ b/drivers/net/wireless/rtlwifi/core.c @@ -504,7 +504,8 @@ static int _rtl_get_hal_qnum(u16 queue) *for mac80211 VO=0, VI=1, BE=2, BK=3 *for rtl819x BE=0, BK=1, VI=2, VO=3 */ -static int rtl_op_conf_tx(struct ieee80211_hw *hw, u16 queue, +static int rtl_op_conf_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u16 queue, const struct ieee80211_tx_queue_params *param) { struct rtl_priv *rtlpriv = rtl_priv(hw); diff --git a/drivers/net/wireless/wl1251/main.c b/drivers/net/wireless/wl1251/main.c index a14a48c99cdc..ba3268ea81fe 100644 --- a/drivers/net/wireless/wl1251/main.c +++ b/drivers/net/wireless/wl1251/main.c @@ -1158,7 +1158,8 @@ static struct ieee80211_channel wl1251_channels[] = { { .hw_value = 13, .center_freq = 2472}, }; -static int wl1251_op_conf_tx(struct ieee80211_hw *hw, u16 queue, +static int wl1251_op_conf_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u16 queue, const struct ieee80211_tx_queue_params *params) { enum wl1251_acx_ps_scheme ps_scheme; diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 7d409b0f3357..e2d6edd2fcd2 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -3744,7 +3744,8 @@ out: mutex_unlock(&wl->mutex); } -static int wl1271_op_conf_tx(struct ieee80211_hw *hw, u16 queue, +static int wl1271_op_conf_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u16 queue, const struct ieee80211_tx_queue_params *params) { struct wl1271 *wl = hw->priv; diff --git a/drivers/staging/brcm80211/brcmsmac/mac80211_if.c b/drivers/staging/brcm80211/brcmsmac/mac80211_if.c index 315dd91800b6..6d71cba3a0a5 100644 --- a/drivers/staging/brcm80211/brcmsmac/mac80211_if.c +++ b/drivers/staging/brcm80211/brcmsmac/mac80211_if.c @@ -141,8 +141,9 @@ static void brcms_ops_sta_notify(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum sta_notify_cmd cmd, struct ieee80211_sta *sta); -static int brcms_ops_conf_tx(struct ieee80211_hw *hw, u16 queue, - const struct ieee80211_tx_queue_params *params); +static int brcms_ops_conf_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u16 queue, + const struct ieee80211_tx_queue_params *params); static u64 brcms_ops_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif); static int brcms_ops_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, @@ -556,7 +557,7 @@ brcms_ops_sta_notify(struct ieee80211_hw *hw, struct ieee80211_vif *vif, } static int -brcms_ops_conf_tx(struct ieee80211_hw *hw, u16 queue, +brcms_ops_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 queue, const struct ieee80211_tx_queue_params *params) { struct brcms_info *wl = hw->priv; diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 135e897b61c7..cd108dfa1952 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -2152,7 +2152,8 @@ struct ieee80211_ops { struct ieee80211_sta *sta); void (*sta_notify)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum sta_notify_cmd, struct ieee80211_sta *sta); - int (*conf_tx)(struct ieee80211_hw *hw, u16 queue, + int (*conf_tx)(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u16 queue, const struct ieee80211_tx_queue_params *params); u64 (*get_tsf)(struct ieee80211_hw *hw, struct ieee80211_vif *vif); void (*set_tsf)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 68721d379fe1..5f165d7eb2db 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -423,7 +423,8 @@ static inline int drv_conf_tx(struct ieee80211_local *local, trace_drv_conf_tx(local, sdata, queue, params); if (local->ops->conf_tx) - ret = local->ops->conf_tx(&local->hw, queue, params); + ret = local->ops->conf_tx(&local->hw, &sdata->vif, + queue, params); trace_drv_return_int(local, ret); return ret; } -- cgit v1.2.3