aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohannes Berg <johannes@sipsolutions.net>2009-12-01 07:37:02 -0500
committerJohn W. Linville <linville@tuxdriver.com>2009-12-22 13:31:16 -0500
commit0f78231bffb868a30e8533aace142213266bb811 (patch)
tree317f65dc6d89e9a89ad83f94fadd780dd1e0ca83
parent18974b5b0b5e758d416c550553b143e5c8038281 (diff)
mac80211: enable spatial multiplexing powersave
Enable spatial multiplexing in mac80211 by telling the driver what to do and, where necessary, sending action frames to the AP to update the requested SMPS mode. Also includes a trivial implementation for hwsim that just logs the requested mode. For now, the userspace interface is in debugfs only, and let you toggle the requested mode at any time. Signed-off-by: Johannes Berg <johannes@sipsolutions.net> Signed-off-by: John W. Linville <linville@tuxdriver.com>
-rw-r--r--drivers/net/wireless/mac80211_hwsim.c24
-rw-r--r--include/linux/ieee80211.h25
-rw-r--r--include/net/mac80211.h59
-rw-r--r--net/mac80211/cfg.c49
-rw-r--r--net/mac80211/debugfs_netdev.c111
-rw-r--r--net/mac80211/driver-trace.h2
-rw-r--r--net/mac80211/ht.c47
-rw-r--r--net/mac80211/ieee80211_i.h14
-rw-r--r--net/mac80211/main.c24
-rw-r--r--net/mac80211/mlme.c63
-rw-r--r--net/mac80211/status.c38
-rw-r--r--net/mac80211/util.c74
12 files changed, 518 insertions, 12 deletions
diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c
index 88e41176e7fd..92c669ebb358 100644
--- a/drivers/net/wireless/mac80211_hwsim.c
+++ b/drivers/net/wireless/mac80211_hwsim.c
@@ -618,12 +618,26 @@ static int mac80211_hwsim_config(struct ieee80211_hw *hw, u32 changed)
618{ 618{
619 struct mac80211_hwsim_data *data = hw->priv; 619 struct mac80211_hwsim_data *data = hw->priv;
620 struct ieee80211_conf *conf = &hw->conf; 620 struct ieee80211_conf *conf = &hw->conf;
621 621 static const char *chantypes[4] = {
622 printk(KERN_DEBUG "%s:%s (freq=%d idle=%d ps=%d)\n", 622 [NL80211_CHAN_NO_HT] = "noht",
623 [NL80211_CHAN_HT20] = "ht20",
624 [NL80211_CHAN_HT40MINUS] = "ht40-",
625 [NL80211_CHAN_HT40PLUS] = "ht40+",
626 };
627 static const char *smps_modes[IEEE80211_SMPS_NUM_MODES] = {
628 [IEEE80211_SMPS_AUTOMATIC] = "auto",
629 [IEEE80211_SMPS_OFF] = "off",
630 [IEEE80211_SMPS_STATIC] = "static",
631 [IEEE80211_SMPS_DYNAMIC] = "dynamic",
632 };
633
634 printk(KERN_DEBUG "%s:%s (freq=%d/%s idle=%d ps=%d smps=%s)\n",
623 wiphy_name(hw->wiphy), __func__, 635 wiphy_name(hw->wiphy), __func__,
624 conf->channel->center_freq, 636 conf->channel->center_freq,
637 chantypes[conf->channel_type],
625 !!(conf->flags & IEEE80211_CONF_IDLE), 638 !!(conf->flags & IEEE80211_CONF_IDLE),
626 !!(conf->flags & IEEE80211_CONF_PS)); 639 !!(conf->flags & IEEE80211_CONF_PS),
640 smps_modes[conf->smps_mode]);
627 641
628 data->idle = !!(conf->flags & IEEE80211_CONF_IDLE); 642 data->idle = !!(conf->flags & IEEE80211_CONF_IDLE);
629 643
@@ -1082,7 +1096,9 @@ static int __init init_mac80211_hwsim(void)
1082 BIT(NL80211_IFTYPE_MESH_POINT); 1096 BIT(NL80211_IFTYPE_MESH_POINT);
1083 1097
1084 hw->flags = IEEE80211_HW_MFP_CAPABLE | 1098 hw->flags = IEEE80211_HW_MFP_CAPABLE |
1085 IEEE80211_HW_SIGNAL_DBM; 1099 IEEE80211_HW_SIGNAL_DBM |
1100 IEEE80211_HW_SUPPORTS_STATIC_SMPS |
1101 IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS;
1086 1102
1087 /* ask mac80211 to reserve space for magic */ 1103 /* ask mac80211 to reserve space for magic */
1088 hw->vif_data_size = sizeof(struct hwsim_vif_priv); 1104 hw->vif_data_size = sizeof(struct hwsim_vif_priv);
diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
index d9724a28c0c2..e8d43d0ff2c3 100644
--- a/include/linux/ieee80211.h
+++ b/include/linux/ieee80211.h
@@ -707,6 +707,10 @@ struct ieee80211_mgmt {
707 u8 action; 707 u8 action;
708 u8 trans_id[WLAN_SA_QUERY_TR_ID_LEN]; 708 u8 trans_id[WLAN_SA_QUERY_TR_ID_LEN];
709 } __attribute__ ((packed)) sa_query; 709 } __attribute__ ((packed)) sa_query;
710 struct {
711 u8 action;
712 u8 smps_control;
713 } __attribute__ ((packed)) ht_smps;
710 } u; 714 } u;
711 } __attribute__ ((packed)) action; 715 } __attribute__ ((packed)) action;
712 } u; 716 } u;
@@ -824,6 +828,7 @@ struct ieee80211_ht_cap {
824#define IEEE80211_HT_CAP_LDPC_CODING 0x0001 828#define IEEE80211_HT_CAP_LDPC_CODING 0x0001
825#define IEEE80211_HT_CAP_SUP_WIDTH_20_40 0x0002 829#define IEEE80211_HT_CAP_SUP_WIDTH_20_40 0x0002
826#define IEEE80211_HT_CAP_SM_PS 0x000C 830#define IEEE80211_HT_CAP_SM_PS 0x000C
831#define IEEE80211_HT_CAP_SM_PS_SHIFT 2
827#define IEEE80211_HT_CAP_GRN_FLD 0x0010 832#define IEEE80211_HT_CAP_GRN_FLD 0x0010
828#define IEEE80211_HT_CAP_SGI_20 0x0020 833#define IEEE80211_HT_CAP_SGI_20 0x0020
829#define IEEE80211_HT_CAP_SGI_40 0x0040 834#define IEEE80211_HT_CAP_SGI_40 0x0040
@@ -839,6 +844,7 @@ struct ieee80211_ht_cap {
839/* 802.11n HT capability AMPDU settings (for ampdu_params_info) */ 844/* 802.11n HT capability AMPDU settings (for ampdu_params_info) */
840#define IEEE80211_HT_AMPDU_PARM_FACTOR 0x03 845#define IEEE80211_HT_AMPDU_PARM_FACTOR 0x03
841#define IEEE80211_HT_AMPDU_PARM_DENSITY 0x1C 846#define IEEE80211_HT_AMPDU_PARM_DENSITY 0x1C
847#define IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT 2
842 848
843/* 849/*
844 * Maximum length of AMPDU that the STA can receive. 850 * Maximum length of AMPDU that the STA can receive.
@@ -922,12 +928,17 @@ struct ieee80211_ht_info {
922#define IEEE80211_MAX_AMPDU_BUF 0x40 928#define IEEE80211_MAX_AMPDU_BUF 0x40
923 929
924 930
925/* Spatial Multiplexing Power Save Modes */ 931/* Spatial Multiplexing Power Save Modes (for capability) */
926#define WLAN_HT_CAP_SM_PS_STATIC 0 932#define WLAN_HT_CAP_SM_PS_STATIC 0
927#define WLAN_HT_CAP_SM_PS_DYNAMIC 1 933#define WLAN_HT_CAP_SM_PS_DYNAMIC 1
928#define WLAN_HT_CAP_SM_PS_INVALID 2 934#define WLAN_HT_CAP_SM_PS_INVALID 2
929#define WLAN_HT_CAP_SM_PS_DISABLED 3 935#define WLAN_HT_CAP_SM_PS_DISABLED 3
930 936
937/* for SM power control field lower two bits */
938#define WLAN_HT_SMPS_CONTROL_DISABLED 0
939#define WLAN_HT_SMPS_CONTROL_STATIC 1
940#define WLAN_HT_SMPS_CONTROL_DYNAMIC 3
941
931/* Authentication algorithms */ 942/* Authentication algorithms */
932#define WLAN_AUTH_OPEN 0 943#define WLAN_AUTH_OPEN 0
933#define WLAN_AUTH_SHARED_KEY 1 944#define WLAN_AUTH_SHARED_KEY 1
@@ -1150,6 +1161,18 @@ enum ieee80211_spectrum_mgmt_actioncode {
1150 WLAN_ACTION_SPCT_CHL_SWITCH = 4, 1161 WLAN_ACTION_SPCT_CHL_SWITCH = 4,
1151}; 1162};
1152 1163
1164/* HT action codes */
1165enum ieee80211_ht_actioncode {
1166 WLAN_HT_ACTION_NOTIFY_CHANWIDTH = 0,
1167 WLAN_HT_ACTION_SMPS = 1,
1168 WLAN_HT_ACTION_PSMP = 2,
1169 WLAN_HT_ACTION_PCO_PHASE = 3,
1170 WLAN_HT_ACTION_CSI = 4,
1171 WLAN_HT_ACTION_NONCOMPRESSED_BF = 5,
1172 WLAN_HT_ACTION_COMPRESSED_BF = 6,
1173 WLAN_HT_ACTION_ASEL_IDX_FEEDBACK = 7,
1174};
1175
1153/* Security key length */ 1176/* Security key length */
1154enum ieee80211_key_len { 1177enum ieee80211_key_len {
1155 WLAN_KEY_LEN_WEP40 = 5, 1178 WLAN_KEY_LEN_WEP40 = 5,
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index e94cc526b0f6..e6b6bf81d5b9 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -597,8 +597,10 @@ enum ieee80211_conf_flags {
597 * @IEEE80211_CONF_CHANGE_CHANNEL: the channel/channel_type changed 597 * @IEEE80211_CONF_CHANGE_CHANNEL: the channel/channel_type changed
598 * @IEEE80211_CONF_CHANGE_RETRY_LIMITS: retry limits changed 598 * @IEEE80211_CONF_CHANGE_RETRY_LIMITS: retry limits changed
599 * @IEEE80211_CONF_CHANGE_IDLE: Idle flag changed 599 * @IEEE80211_CONF_CHANGE_IDLE: Idle flag changed
600 * @IEEE80211_CONF_CHANGE_SMPS: Spatial multiplexing powersave mode changed
600 */ 601 */
601enum ieee80211_conf_changed { 602enum ieee80211_conf_changed {
603 IEEE80211_CONF_CHANGE_SMPS = BIT(1),
602 IEEE80211_CONF_CHANGE_LISTEN_INTERVAL = BIT(2), 604 IEEE80211_CONF_CHANGE_LISTEN_INTERVAL = BIT(2),
603 IEEE80211_CONF_CHANGE_MONITOR = BIT(3), 605 IEEE80211_CONF_CHANGE_MONITOR = BIT(3),
604 IEEE80211_CONF_CHANGE_PS = BIT(4), 606 IEEE80211_CONF_CHANGE_PS = BIT(4),
@@ -609,6 +611,21 @@ enum ieee80211_conf_changed {
609}; 611};
610 612
611/** 613/**
614 * enum ieee80211_smps_mode - spatial multiplexing power save mode
615 *
616 * @
617 */
618enum ieee80211_smps_mode {
619 IEEE80211_SMPS_AUTOMATIC,
620 IEEE80211_SMPS_OFF,
621 IEEE80211_SMPS_STATIC,
622 IEEE80211_SMPS_DYNAMIC,
623
624 /* keep last */
625 IEEE80211_SMPS_NUM_MODES,
626};
627
628/**
612 * struct ieee80211_conf - configuration of the device 629 * struct ieee80211_conf - configuration of the device
613 * 630 *
614 * This struct indicates how the driver shall configure the hardware. 631 * This struct indicates how the driver shall configure the hardware.
@@ -636,6 +653,10 @@ enum ieee80211_conf_changed {
636 * @short_frame_max_tx_count: Maximum number of transmissions for a "short" 653 * @short_frame_max_tx_count: Maximum number of transmissions for a "short"
637 * frame, called "dot11ShortRetryLimit" in 802.11, but actually means the 654 * frame, called "dot11ShortRetryLimit" in 802.11, but actually means the
638 * number of transmissions not the number of retries 655 * number of transmissions not the number of retries
656 *
657 * @smps_mode: spatial multiplexing powersave mode; note that
658 * %IEEE80211_SMPS_STATIC is used when the device is not
659 * configured for an HT channel
639 */ 660 */
640struct ieee80211_conf { 661struct ieee80211_conf {
641 u32 flags; 662 u32 flags;
@@ -648,6 +669,7 @@ struct ieee80211_conf {
648 669
649 struct ieee80211_channel *channel; 670 struct ieee80211_channel *channel;
650 enum nl80211_channel_type channel_type; 671 enum nl80211_channel_type channel_type;
672 enum ieee80211_smps_mode smps_mode;
651}; 673};
652 674
653/** 675/**
@@ -930,6 +952,16 @@ enum ieee80211_tkip_key_type {
930 * @IEEE80211_HW_BEACON_FILTER: 952 * @IEEE80211_HW_BEACON_FILTER:
931 * Hardware supports dropping of irrelevant beacon frames to 953 * Hardware supports dropping of irrelevant beacon frames to
932 * avoid waking up cpu. 954 * avoid waking up cpu.
955 *
956 * @IEEE80211_HW_SUPPORTS_STATIC_SMPS:
957 * Hardware supports static spatial multiplexing powersave,
958 * ie. can turn off all but one chain even on HT connections
959 * that should be using more chains.
960 *
961 * @IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS:
962 * Hardware supports dynamic spatial multiplexing powersave,
963 * ie. can turn off all but one chain and then wake the rest
964 * up as required after, for example, rts/cts handshake.
933 */ 965 */
934enum ieee80211_hw_flags { 966enum ieee80211_hw_flags {
935 IEEE80211_HW_HAS_RATE_CONTROL = 1<<0, 967 IEEE80211_HW_HAS_RATE_CONTROL = 1<<0,
@@ -947,6 +979,8 @@ enum ieee80211_hw_flags {
947 IEEE80211_HW_SUPPORTS_DYNAMIC_PS = 1<<12, 979 IEEE80211_HW_SUPPORTS_DYNAMIC_PS = 1<<12,
948 IEEE80211_HW_MFP_CAPABLE = 1<<13, 980 IEEE80211_HW_MFP_CAPABLE = 1<<13,
949 IEEE80211_HW_BEACON_FILTER = 1<<14, 981 IEEE80211_HW_BEACON_FILTER = 1<<14,
982 IEEE80211_HW_SUPPORTS_STATIC_SMPS = 1<<15,
983 IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS = 1<<16,
950}; 984};
951 985
952/** 986/**
@@ -1215,6 +1249,31 @@ ieee80211_get_alt_retry_rate(const struct ieee80211_hw *hw,
1215 */ 1249 */
1216 1250
1217/** 1251/**
1252 * DOC: Spatial multiplexing power save
1253 *
1254 * SMPS (Spatial multiplexing power save) is a mechanism to conserve
1255 * power in an 802.11n implementation. For details on the mechanism
1256 * and rationale, please refer to 802.11 (as amended by 802.11n-2009)
1257 * "11.2.3 SM power save".
1258 *
1259 * The mac80211 implementation is capable of sending action frames
1260 * to update the AP about the station's SMPS mode, and will instruct
1261 * the driver to enter the specific mode. It will also announce the
1262 * requested SMPS mode during the association handshake. Hardware
1263 * support for this feature is required, and can be indicated by
1264 * hardware flags.
1265 *
1266 * The default mode will be "automatic", which nl80211/cfg80211
1267 * defines to be dynamic SMPS in (regular) powersave, and SMPS
1268 * turned off otherwise.
1269 *
1270 * To support this feature, the driver must set the appropriate
1271 * hardware support flags, and handle the SMPS flag to the config()
1272 * operation. It will then with this mechanism be instructed to
1273 * enter the requested SMPS mode while associated to an HT AP.
1274 */
1275
1276/**
1218 * DOC: Frame filtering 1277 * DOC: Frame filtering
1219 * 1278 *
1220 * mac80211 requires to see many management frames for proper 1279 * mac80211 requires to see many management frames for proper
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index fcfa1bf776a7..8c35418d1c96 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -1318,6 +1318,50 @@ static int ieee80211_testmode_cmd(struct wiphy *wiphy, void *data, int len)
1318} 1318}
1319#endif 1319#endif
1320 1320
1321int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata,
1322 enum ieee80211_smps_mode smps_mode)
1323{
1324 const u8 *ap;
1325 enum ieee80211_smps_mode old_req;
1326 int err;
1327
1328 old_req = sdata->u.mgd.req_smps;
1329 sdata->u.mgd.req_smps = smps_mode;
1330
1331 if (old_req == smps_mode &&
1332 smps_mode != IEEE80211_SMPS_AUTOMATIC)
1333 return 0;
1334
1335 /*
1336 * If not associated, or current association is not an HT
1337 * association, there's no need to send an action frame.
1338 */
1339 if (!sdata->u.mgd.associated ||
1340 sdata->local->oper_channel_type == NL80211_CHAN_NO_HT) {
1341 mutex_lock(&sdata->local->iflist_mtx);
1342 ieee80211_recalc_smps(sdata->local, sdata);
1343 mutex_unlock(&sdata->local->iflist_mtx);
1344 return 0;
1345 }
1346
1347 ap = sdata->u.mgd.associated->cbss.bssid;
1348
1349 if (smps_mode == IEEE80211_SMPS_AUTOMATIC) {
1350 if (sdata->u.mgd.powersave)
1351 smps_mode = IEEE80211_SMPS_DYNAMIC;
1352 else
1353 smps_mode = IEEE80211_SMPS_OFF;
1354 }
1355
1356 /* send SM PS frame to AP */
1357 err = ieee80211_send_smps_action(sdata, smps_mode,
1358 ap, ap);
1359 if (err)
1360 sdata->u.mgd.req_smps = old_req;
1361
1362 return err;
1363}
1364
1321static int ieee80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev, 1365static int ieee80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev,
1322 bool enabled, int timeout) 1366 bool enabled, int timeout)
1323{ 1367{
@@ -1335,6 +1379,11 @@ static int ieee80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev,
1335 sdata->u.mgd.powersave = enabled; 1379 sdata->u.mgd.powersave = enabled;
1336 conf->dynamic_ps_timeout = timeout; 1380 conf->dynamic_ps_timeout = timeout;
1337 1381
1382 /* no change, but if automatic follow powersave */
1383 mutex_lock(&sdata->u.mgd.mtx);
1384 __ieee80211_request_smps(sdata, sdata->u.mgd.req_smps);
1385 mutex_unlock(&sdata->u.mgd.mtx);
1386
1338 if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS) 1387 if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS)
1339 ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS); 1388 ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
1340 1389
diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c
index 5d9c797635a9..355983503885 100644
--- a/net/mac80211/debugfs_netdev.c
+++ b/net/mac80211/debugfs_netdev.c
@@ -41,6 +41,30 @@ static ssize_t ieee80211_if_read(
41 return ret; 41 return ret;
42} 42}
43 43
44static ssize_t ieee80211_if_write(
45 struct ieee80211_sub_if_data *sdata,
46 const char __user *userbuf,
47 size_t count, loff_t *ppos,
48 ssize_t (*write)(struct ieee80211_sub_if_data *, const char *, int))
49{
50 u8 *buf;
51 ssize_t ret = -ENODEV;
52
53 buf = kzalloc(count, GFP_KERNEL);
54 if (!buf)
55 return -ENOMEM;
56
57 if (copy_from_user(buf, userbuf, count))
58 return -EFAULT;
59
60 rtnl_lock();
61 if (sdata->dev->reg_state == NETREG_REGISTERED)
62 ret = (*write)(sdata, buf, count);
63 rtnl_unlock();
64
65 return ret;
66}
67
44#define IEEE80211_IF_FMT(name, field, format_string) \ 68#define IEEE80211_IF_FMT(name, field, format_string) \
45static ssize_t ieee80211_if_fmt_##name( \ 69static ssize_t ieee80211_if_fmt_##name( \
46 const struct ieee80211_sub_if_data *sdata, char *buf, \ 70 const struct ieee80211_sub_if_data *sdata, char *buf, \
@@ -71,7 +95,7 @@ static ssize_t ieee80211_if_fmt_##name( \
71 return scnprintf(buf, buflen, "%pM\n", sdata->field); \ 95 return scnprintf(buf, buflen, "%pM\n", sdata->field); \
72} 96}
73 97
74#define __IEEE80211_IF_FILE(name) \ 98#define __IEEE80211_IF_FILE(name, _write) \
75static ssize_t ieee80211_if_read_##name(struct file *file, \ 99static ssize_t ieee80211_if_read_##name(struct file *file, \
76 char __user *userbuf, \ 100 char __user *userbuf, \
77 size_t count, loff_t *ppos) \ 101 size_t count, loff_t *ppos) \
@@ -82,12 +106,24 @@ static ssize_t ieee80211_if_read_##name(struct file *file, \
82} \ 106} \
83static const struct file_operations name##_ops = { \ 107static const struct file_operations name##_ops = { \
84 .read = ieee80211_if_read_##name, \ 108 .read = ieee80211_if_read_##name, \
109 .write = (_write), \
85 .open = mac80211_open_file_generic, \ 110 .open = mac80211_open_file_generic, \
86} 111}
87 112
113#define __IEEE80211_IF_FILE_W(name) \
114static ssize_t ieee80211_if_write_##name(struct file *file, \
115 const char __user *userbuf, \
116 size_t count, loff_t *ppos) \
117{ \
118 return ieee80211_if_write(file->private_data, userbuf, count, \
119 ppos, ieee80211_if_parse_##name); \
120} \
121__IEEE80211_IF_FILE(name, ieee80211_if_write_##name)
122
123
88#define IEEE80211_IF_FILE(name, field, format) \ 124#define IEEE80211_IF_FILE(name, field, format) \
89 IEEE80211_IF_FMT_##format(name, field) \ 125 IEEE80211_IF_FMT_##format(name, field) \
90 __IEEE80211_IF_FILE(name) 126 __IEEE80211_IF_FILE(name, NULL)
91 127
92/* common attributes */ 128/* common attributes */
93IEEE80211_IF_FILE(drop_unencrypted, drop_unencrypted, DEC); 129IEEE80211_IF_FILE(drop_unencrypted, drop_unencrypted, DEC);
@@ -99,6 +135,70 @@ IEEE80211_IF_FILE(bssid, u.mgd.bssid, MAC);
99IEEE80211_IF_FILE(aid, u.mgd.aid, DEC); 135IEEE80211_IF_FILE(aid, u.mgd.aid, DEC);
100IEEE80211_IF_FILE(capab, u.mgd.capab, HEX); 136IEEE80211_IF_FILE(capab, u.mgd.capab, HEX);
101 137
138static int ieee80211_set_smps(struct ieee80211_sub_if_data *sdata,
139 enum ieee80211_smps_mode smps_mode)
140{
141 struct ieee80211_local *local = sdata->local;
142 int err;
143
144 if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_STATIC_SMPS) &&
145 smps_mode == IEEE80211_SMPS_STATIC)
146 return -EINVAL;
147
148 /* auto should be dynamic if in PS mode */
149 if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS) &&
150 (smps_mode == IEEE80211_SMPS_DYNAMIC ||
151 smps_mode == IEEE80211_SMPS_AUTOMATIC))
152 return -EINVAL;
153
154 /* supported only on managed interfaces for now */
155 if (sdata->vif.type != NL80211_IFTYPE_STATION)
156 return -EOPNOTSUPP;
157
158 mutex_lock(&local->iflist_mtx);
159 err = __ieee80211_request_smps(sdata, smps_mode);
160 mutex_unlock(&local->iflist_mtx);
161
162 return err;
163}
164
165static const char *smps_modes[IEEE80211_SMPS_NUM_MODES] = {
166 [IEEE80211_SMPS_AUTOMATIC] = "auto",
167 [IEEE80211_SMPS_OFF] = "off",
168 [IEEE80211_SMPS_STATIC] = "static",
169 [IEEE80211_SMPS_DYNAMIC] = "dynamic",
170};
171
172static ssize_t ieee80211_if_fmt_smps(const struct ieee80211_sub_if_data *sdata,
173 char *buf, int buflen)
174{
175 if (sdata->vif.type != NL80211_IFTYPE_STATION)
176 return -EOPNOTSUPP;
177
178 return snprintf(buf, buflen, "request: %s\nused: %s\n",
179 smps_modes[sdata->u.mgd.req_smps],
180 smps_modes[sdata->u.mgd.ap_smps]);
181}
182
183static ssize_t ieee80211_if_parse_smps(struct ieee80211_sub_if_data *sdata,
184 const char *buf, int buflen)
185{
186 enum ieee80211_smps_mode mode;
187
188 for (mode = 0; mode < IEEE80211_SMPS_NUM_MODES; mode++) {
189 if (strncmp(buf, smps_modes[mode], buflen) == 0) {
190 int err = ieee80211_set_smps(sdata, mode);
191 if (!err)
192 return buflen;
193 return err;
194 }
195 }
196
197 return -EINVAL;
198}
199
200__IEEE80211_IF_FILE_W(smps);
201
102/* AP attributes */ 202/* AP attributes */
103IEEE80211_IF_FILE(num_sta_ps, u.ap.num_sta_ps, ATOMIC); 203IEEE80211_IF_FILE(num_sta_ps, u.ap.num_sta_ps, ATOMIC);
104IEEE80211_IF_FILE(dtim_count, u.ap.dtim_count, DEC); 204IEEE80211_IF_FILE(dtim_count, u.ap.dtim_count, DEC);
@@ -109,7 +209,7 @@ static ssize_t ieee80211_if_fmt_num_buffered_multicast(
109 return scnprintf(buf, buflen, "%u\n", 209 return scnprintf(buf, buflen, "%u\n",
110 skb_queue_len(&sdata->u.ap.ps_bc_buf)); 210 skb_queue_len(&sdata->u.ap.ps_bc_buf));
111} 211}
112__IEEE80211_IF_FILE(num_buffered_multicast); 212__IEEE80211_IF_FILE(num_buffered_multicast, NULL);
113 213
114/* WDS attributes */ 214/* WDS attributes */
115IEEE80211_IF_FILE(peer, u.wds.remote_addr, MAC); 215IEEE80211_IF_FILE(peer, u.wds.remote_addr, MAC);
@@ -158,6 +258,10 @@ IEEE80211_IF_FILE(dot11MeshHWMPRootMode,
158 debugfs_create_file(#name, 0400, sdata->debugfs.dir, \ 258 debugfs_create_file(#name, 0400, sdata->debugfs.dir, \
159 sdata, &name##_ops); 259 sdata, &name##_ops);
160 260
261#define DEBUGFS_ADD_MODE(name, mode) \
262 debugfs_create_file(#name, mode, sdata->debugfs.dir, \
263 sdata, &name##_ops);
264
161static void add_sta_files(struct ieee80211_sub_if_data *sdata) 265static void add_sta_files(struct ieee80211_sub_if_data *sdata)
162{ 266{
163 DEBUGFS_ADD(drop_unencrypted, sta); 267 DEBUGFS_ADD(drop_unencrypted, sta);
@@ -167,6 +271,7 @@ static void add_sta_files(struct ieee80211_sub_if_data *sdata)
167 DEBUGFS_ADD(bssid, sta); 271 DEBUGFS_ADD(bssid, sta);
168 DEBUGFS_ADD(aid, sta); 272 DEBUGFS_ADD(aid, sta);
169 DEBUGFS_ADD(capab, sta); 273 DEBUGFS_ADD(capab, sta);
274 DEBUGFS_ADD_MODE(smps, 0600);
170} 275}
171 276
172static void add_ap_files(struct ieee80211_sub_if_data *sdata) 277static void add_ap_files(struct ieee80211_sub_if_data *sdata)
diff --git a/net/mac80211/driver-trace.h b/net/mac80211/driver-trace.h
index ee2d19a25ce1..7a849b920165 100644
--- a/net/mac80211/driver-trace.h
+++ b/net/mac80211/driver-trace.h
@@ -140,6 +140,7 @@ TRACE_EVENT(drv_config,
140 __field(u8, short_frame_max_tx_count) 140 __field(u8, short_frame_max_tx_count)
141 __field(int, center_freq) 141 __field(int, center_freq)
142 __field(int, channel_type) 142 __field(int, channel_type)
143 __field(int, smps)
143 ), 144 ),
144 145
145 TP_fast_assign( 146 TP_fast_assign(
@@ -155,6 +156,7 @@ TRACE_EVENT(drv_config,
155 __entry->short_frame_max_tx_count = local->hw.conf.short_frame_max_tx_count; 156 __entry->short_frame_max_tx_count = local->hw.conf.short_frame_max_tx_count;
156 __entry->center_freq = local->hw.conf.channel->center_freq; 157 __entry->center_freq = local->hw.conf.channel->center_freq;
157 __entry->channel_type = local->hw.conf.channel_type; 158 __entry->channel_type = local->hw.conf.channel_type;
159 __entry->smps = local->hw.conf.smps_mode;
158 ), 160 ),
159 161
160 TP_printk( 162 TP_printk(
diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c
index 45ebd062a2fb..63b8f86b7f16 100644
--- a/net/mac80211/ht.c
+++ b/net/mac80211/ht.c
@@ -166,3 +166,50 @@ void ieee80211_process_delba(struct ieee80211_sub_if_data *sdata,
166 spin_unlock_bh(&sta->lock); 166 spin_unlock_bh(&sta->lock);
167 } 167 }
168} 168}
169
170int ieee80211_send_smps_action(struct ieee80211_sub_if_data *sdata,
171 enum ieee80211_smps_mode smps, const u8 *da,
172 const u8 *bssid)
173{
174 struct ieee80211_local *local = sdata->local;
175 struct sk_buff *skb;
176 struct ieee80211_mgmt *action_frame;
177
178 /* 27 = header + category + action + smps mode */
179 skb = dev_alloc_skb(27 + local->hw.extra_tx_headroom);
180 if (!skb)
181 return -ENOMEM;
182
183 skb_reserve(skb, local->hw.extra_tx_headroom);
184 action_frame = (void *)skb_put(skb, 27);
185 memcpy(action_frame->da, da, ETH_ALEN);
186 memcpy(action_frame->sa, sdata->dev->dev_addr, ETH_ALEN);
187 memcpy(action_frame->bssid, bssid, ETH_ALEN);
188 action_frame->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
189 IEEE80211_STYPE_ACTION);
190 action_frame->u.action.category = WLAN_CATEGORY_HT;
191 action_frame->u.action.u.ht_smps.action = WLAN_HT_ACTION_SMPS;
192 switch (smps) {
193 case IEEE80211_SMPS_AUTOMATIC:
194 case IEEE80211_SMPS_NUM_MODES:
195 WARN_ON(1);
196 case IEEE80211_SMPS_OFF:
197 action_frame->u.action.u.ht_smps.smps_control =
198 WLAN_HT_SMPS_CONTROL_DISABLED;
199 break;
200 case IEEE80211_SMPS_STATIC:
201 action_frame->u.action.u.ht_smps.smps_control =
202 WLAN_HT_SMPS_CONTROL_STATIC;
203 break;
204 case IEEE80211_SMPS_DYNAMIC:
205 action_frame->u.action.u.ht_smps.smps_control =
206 WLAN_HT_SMPS_CONTROL_DYNAMIC;
207 break;
208 }
209
210 /* we'll do more on status of this frame */
211 IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS;
212 ieee80211_tx_skb(sdata, skb);
213
214 return 0;
215}
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 178e329f9257..e63aecbddfbe 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -297,6 +297,8 @@ struct ieee80211_if_managed {
297 297
298 unsigned long timers_running; /* used for quiesce/restart */ 298 unsigned long timers_running; /* used for quiesce/restart */
299 bool powersave; /* powersave requested for this iface */ 299 bool powersave; /* powersave requested for this iface */
300 enum ieee80211_smps_mode req_smps, /* requested smps mode */
301 ap_smps; /* smps mode AP thinks we're in */
300 302
301 unsigned long request; 303 unsigned long request;
302 304
@@ -587,6 +589,9 @@ struct ieee80211_local {
587 /* used for uploading changed mc list */ 589 /* used for uploading changed mc list */
588 struct work_struct reconfig_filter; 590 struct work_struct reconfig_filter;
589 591
592 /* used to reconfigure hardware SM PS */
593 struct work_struct recalc_smps;
594
590 /* aggregated multicast list */ 595 /* aggregated multicast list */
591 struct dev_addr_list *mc_list; 596 struct dev_addr_list *mc_list;
592 int mc_count; 597 int mc_count;
@@ -760,6 +765,8 @@ struct ieee80211_local {
760 int user_power_level; /* in dBm */ 765 int user_power_level; /* in dBm */
761 int power_constr_level; /* in dBm */ 766 int power_constr_level; /* in dBm */
762 767
768 enum ieee80211_smps_mode smps_mode;
769
763 struct work_struct restart_work; 770 struct work_struct restart_work;
764 771
765#ifdef CONFIG_MAC80211_DEBUGFS 772#ifdef CONFIG_MAC80211_DEBUGFS
@@ -978,6 +985,9 @@ void ieee80211_send_bar(struct ieee80211_sub_if_data *sdata, u8 *ra, u16 tid, u1
978void ieee80211_send_delba(struct ieee80211_sub_if_data *sdata, 985void ieee80211_send_delba(struct ieee80211_sub_if_data *sdata,
979 const u8 *da, u16 tid, 986 const u8 *da, u16 tid,
980 u16 initiator, u16 reason_code); 987 u16 initiator, u16 reason_code);
988int ieee80211_send_smps_action(struct ieee80211_sub_if_data *sdata,
989 enum ieee80211_smps_mode smps, const u8 *da,
990 const u8 *bssid);
981 991
982void ieee80211_sta_stop_rx_ba_session(struct ieee80211_sub_if_data *sdata, u8 *da, 992void ieee80211_sta_stop_rx_ba_session(struct ieee80211_sub_if_data *sdata, u8 *da,
983 u16 tid, u16 initiator, u16 reason); 993 u16 tid, u16 initiator, u16 reason);
@@ -1088,6 +1098,10 @@ void ieee80211_sta_def_wmm_params(struct ieee80211_sub_if_data *sdata,
1088u32 ieee80211_sta_get_rates(struct ieee80211_local *local, 1098u32 ieee80211_sta_get_rates(struct ieee80211_local *local,
1089 struct ieee802_11_elems *elems, 1099 struct ieee802_11_elems *elems,
1090 enum ieee80211_band band); 1100 enum ieee80211_band band);
1101int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata,
1102 enum ieee80211_smps_mode smps_mode);
1103void ieee80211_recalc_smps(struct ieee80211_local *local,
1104 struct ieee80211_sub_if_data *forsdata);
1091 1105
1092#ifdef CONFIG_MAC80211_NOINLINE 1106#ifdef CONFIG_MAC80211_NOINLINE
1093#define debug_noinline noinline 1107#define debug_noinline noinline
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index 98320a94c270..e1293e8ed83a 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -113,6 +113,18 @@ int ieee80211_hw_config(struct ieee80211_local *local, u32 changed)
113 changed |= IEEE80211_CONF_CHANGE_CHANNEL; 113 changed |= IEEE80211_CONF_CHANGE_CHANNEL;
114 } 114 }
115 115
116 if (!conf_is_ht(&local->hw.conf)) {
117 /*
118 * mac80211.h documents that this is only valid
119 * when the channel is set to an HT type, and
120 * that otherwise STATIC is used.
121 */
122 local->hw.conf.smps_mode = IEEE80211_SMPS_STATIC;
123 } else if (local->hw.conf.smps_mode != local->smps_mode) {
124 local->hw.conf.smps_mode = local->smps_mode;
125 changed |= IEEE80211_CONF_CHANGE_SMPS;
126 }
127
116 if (scan_chan) 128 if (scan_chan)
117 power = chan->max_power; 129 power = chan->max_power;
118 else 130 else
@@ -297,6 +309,16 @@ void ieee80211_restart_hw(struct ieee80211_hw *hw)
297} 309}
298EXPORT_SYMBOL(ieee80211_restart_hw); 310EXPORT_SYMBOL(ieee80211_restart_hw);
299 311
312static void ieee80211_recalc_smps_work(struct work_struct *work)
313{
314 struct ieee80211_local *local =
315 container_of(work, struct ieee80211_local, recalc_smps);
316
317 mutex_lock(&local->iflist_mtx);
318 ieee80211_recalc_smps(local, NULL);
319 mutex_unlock(&local->iflist_mtx);
320}
321
300struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, 322struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
301 const struct ieee80211_ops *ops) 323 const struct ieee80211_ops *ops)
302{ 324{
@@ -370,6 +392,8 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
370 INIT_WORK(&local->restart_work, ieee80211_restart_work); 392 INIT_WORK(&local->restart_work, ieee80211_restart_work);
371 393
372 INIT_WORK(&local->reconfig_filter, ieee80211_reconfig_filter); 394 INIT_WORK(&local->reconfig_filter, ieee80211_reconfig_filter);
395 INIT_WORK(&local->recalc_smps, ieee80211_recalc_smps_work);
396 local->smps_mode = IEEE80211_SMPS_OFF;
373 397
374 INIT_WORK(&local->dynamic_ps_enable_work, 398 INIT_WORK(&local->dynamic_ps_enable_work,
375 ieee80211_dynamic_ps_enable_work); 399 ieee80211_dynamic_ps_enable_work);
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index cd5dcc3d8c2b..0a762a9ba4df 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -398,6 +398,8 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata,
398 __le16 tmp; 398 __le16 tmp;
399 u32 flags = local->hw.conf.channel->flags; 399 u32 flags = local->hw.conf.channel->flags;
400 400
401 /* determine capability flags */
402
401 switch (ht_info->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) { 403 switch (ht_info->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
402 case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: 404 case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
403 if (flags & IEEE80211_CHAN_NO_HT40PLUS) { 405 if (flags & IEEE80211_CHAN_NO_HT40PLUS) {
@@ -413,17 +415,64 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata,
413 break; 415 break;
414 } 416 }
415 417
416 tmp = cpu_to_le16(cap); 418 /* set SM PS mode properly */
417 pos = skb_put(skb, sizeof(struct ieee80211_ht_cap)+2); 419 cap &= ~IEEE80211_HT_CAP_SM_PS;
420 /* new association always uses requested smps mode */
421 if (ifmgd->req_smps == IEEE80211_SMPS_AUTOMATIC) {
422 if (ifmgd->powersave)
423 ifmgd->ap_smps = IEEE80211_SMPS_DYNAMIC;
424 else
425 ifmgd->ap_smps = IEEE80211_SMPS_OFF;
426 } else
427 ifmgd->ap_smps = ifmgd->req_smps;
428
429 switch (ifmgd->ap_smps) {
430 case IEEE80211_SMPS_AUTOMATIC:
431 case IEEE80211_SMPS_NUM_MODES:
432 WARN_ON(1);
433 case IEEE80211_SMPS_OFF:
434 cap |= WLAN_HT_CAP_SM_PS_DISABLED <<
435 IEEE80211_HT_CAP_SM_PS_SHIFT;
436 break;
437 case IEEE80211_SMPS_STATIC:
438 cap |= WLAN_HT_CAP_SM_PS_STATIC <<
439 IEEE80211_HT_CAP_SM_PS_SHIFT;
440 break;
441 case IEEE80211_SMPS_DYNAMIC:
442 cap |= WLAN_HT_CAP_SM_PS_DYNAMIC <<
443 IEEE80211_HT_CAP_SM_PS_SHIFT;
444 break;
445 }
446
447 /* reserve and fill IE */
448
449 pos = skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2);
418 *pos++ = WLAN_EID_HT_CAPABILITY; 450 *pos++ = WLAN_EID_HT_CAPABILITY;
419 *pos++ = sizeof(struct ieee80211_ht_cap); 451 *pos++ = sizeof(struct ieee80211_ht_cap);
420 memset(pos, 0, sizeof(struct ieee80211_ht_cap)); 452 memset(pos, 0, sizeof(struct ieee80211_ht_cap));
453
454 /* capability flags */
455 tmp = cpu_to_le16(cap);
421 memcpy(pos, &tmp, sizeof(u16)); 456 memcpy(pos, &tmp, sizeof(u16));
422 pos += sizeof(u16); 457 pos += sizeof(u16);
423 /* TODO: needs a define here for << 2 */ 458
459 /* AMPDU parameters */
424 *pos++ = sband->ht_cap.ampdu_factor | 460 *pos++ = sband->ht_cap.ampdu_factor |
425 (sband->ht_cap.ampdu_density << 2); 461 (sband->ht_cap.ampdu_density <<
462 IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT);
463
464 /* MCS set */
426 memcpy(pos, &sband->ht_cap.mcs, sizeof(sband->ht_cap.mcs)); 465 memcpy(pos, &sband->ht_cap.mcs, sizeof(sband->ht_cap.mcs));
466 pos += sizeof(sband->ht_cap.mcs);
467
468 /* extended capabilities */
469 pos += sizeof(__le16);
470
471 /* BF capabilities */
472 pos += sizeof(__le32);
473
474 /* antenna selection */
475 pos += sizeof(u8);
427 } 476 }
428 477
429 IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; 478 IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
@@ -932,6 +981,7 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
932 981
933 mutex_lock(&local->iflist_mtx); 982 mutex_lock(&local->iflist_mtx);
934 ieee80211_recalc_ps(local, -1); 983 ieee80211_recalc_ps(local, -1);
984 ieee80211_recalc_smps(local, sdata);
935 mutex_unlock(&local->iflist_mtx); 985 mutex_unlock(&local->iflist_mtx);
936 986
937 netif_start_queue(sdata->dev); 987 netif_start_queue(sdata->dev);
@@ -2327,6 +2377,11 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)
2327 ifmgd->flags |= IEEE80211_STA_WMM_ENABLED; 2377 ifmgd->flags |= IEEE80211_STA_WMM_ENABLED;
2328 2378
2329 mutex_init(&ifmgd->mtx); 2379 mutex_init(&ifmgd->mtx);
2380
2381 if (sdata->local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS)
2382 ifmgd->req_smps = IEEE80211_SMPS_AUTOMATIC;
2383 else
2384 ifmgd->req_smps = IEEE80211_SMPS_OFF;
2330} 2385}
2331 2386
2332/* scan finished notification */ 2387/* scan finished notification */
diff --git a/net/mac80211/status.c b/net/mac80211/status.c
index b4608f11a40f..0c0850d37dda 100644
--- a/net/mac80211/status.c
+++ b/net/mac80211/status.c
@@ -134,6 +134,40 @@ static void ieee80211_handle_filtered_frame(struct ieee80211_local *local,
134 dev_kfree_skb(skb); 134 dev_kfree_skb(skb);
135} 135}
136 136
137static void ieee80211_frame_acked(struct sta_info *sta, struct sk_buff *skb)
138{
139 struct ieee80211_mgmt *mgmt = (void *) skb->data;
140 struct ieee80211_local *local = sta->local;
141 struct ieee80211_sub_if_data *sdata = sta->sdata;
142
143 if (ieee80211_is_action(mgmt->frame_control) &&
144 sdata->vif.type == NL80211_IFTYPE_STATION &&
145 mgmt->u.action.category == WLAN_CATEGORY_HT &&
146 mgmt->u.action.u.ht_smps.action == WLAN_HT_ACTION_SMPS) {
147 /*
148 * This update looks racy, but isn't -- if we come
149 * here we've definitely got a station that we're
150 * talking to, and on a managed interface that can
151 * only be the AP. And the only other place updating
152 * this variable is before we're associated.
153 */
154 switch (mgmt->u.action.u.ht_smps.smps_control) {
155 case WLAN_HT_SMPS_CONTROL_DYNAMIC:
156 sta->sdata->u.mgd.ap_smps = IEEE80211_SMPS_DYNAMIC;
157 break;
158 case WLAN_HT_SMPS_CONTROL_STATIC:
159 sta->sdata->u.mgd.ap_smps = IEEE80211_SMPS_STATIC;
160 break;
161 case WLAN_HT_SMPS_CONTROL_DISABLED:
162 default: /* shouldn't happen since we don't send that */
163 sta->sdata->u.mgd.ap_smps = IEEE80211_SMPS_OFF;
164 break;
165 }
166
167 ieee80211_queue_work(&local->hw, &local->recalc_smps);
168 }
169}
170
137void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) 171void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
138{ 172{
139 struct sk_buff *skb2; 173 struct sk_buff *skb2;
@@ -210,6 +244,10 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
210 rate_control_tx_status(local, sband, sta, skb); 244 rate_control_tx_status(local, sband, sta, skb);
211 if (ieee80211_vif_is_mesh(&sta->sdata->vif)) 245 if (ieee80211_vif_is_mesh(&sta->sdata->vif))
212 ieee80211s_update_metric(local, sta, skb); 246 ieee80211s_update_metric(local, sta, skb);
247
248 if (!(info->flags & IEEE80211_TX_CTL_INJECTED) &&
249 (info->flags & IEEE80211_TX_STAT_ACK))
250 ieee80211_frame_acked(sta, skb);
213 } 251 }
214 252
215 rcu_read_unlock(); 253 rcu_read_unlock();
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index d54dbe8e09ba..086ef6257b4b 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -1170,3 +1170,77 @@ int ieee80211_reconfig(struct ieee80211_local *local)
1170 return 0; 1170 return 0;
1171} 1171}
1172 1172
1173static int check_mgd_smps(struct ieee80211_if_managed *ifmgd,
1174 enum ieee80211_smps_mode *smps_mode)
1175{
1176 if (ifmgd->associated) {
1177 *smps_mode = ifmgd->ap_smps;
1178
1179 if (*smps_mode == IEEE80211_SMPS_AUTOMATIC) {
1180 if (ifmgd->powersave)
1181 *smps_mode = IEEE80211_SMPS_DYNAMIC;
1182 else
1183 *smps_mode = IEEE80211_SMPS_OFF;
1184 }
1185
1186 return 1;
1187 }
1188
1189 return 0;
1190}
1191
1192/* must hold iflist_mtx */
1193void ieee80211_recalc_smps(struct ieee80211_local *local,
1194 struct ieee80211_sub_if_data *forsdata)
1195{
1196 struct ieee80211_sub_if_data *sdata;
1197 enum ieee80211_smps_mode smps_mode = IEEE80211_SMPS_OFF;
1198 int count = 0;
1199
1200 if (forsdata)
1201 WARN_ON(!mutex_is_locked(&forsdata->u.mgd.mtx));
1202
1203 WARN_ON(!mutex_is_locked(&local->iflist_mtx));
1204
1205 /*
1206 * This function could be improved to handle multiple
1207 * interfaces better, but right now it makes any
1208 * non-station interfaces force SM PS to be turned
1209 * off. If there are multiple station interfaces it
1210 * could also use the best possible mode, e.g. if
1211 * one is in static and the other in dynamic then
1212 * dynamic is ok.
1213 */
1214
1215 list_for_each_entry(sdata, &local->interfaces, list) {
1216 if (!netif_running(sdata->dev))
1217 continue;
1218 if (sdata->vif.type != NL80211_IFTYPE_STATION)
1219 goto set;
1220 if (sdata != forsdata) {
1221 /*
1222 * This nested is ok -- we are holding the iflist_mtx
1223 * so can't get here twice or so. But it's required
1224 * since normally we acquire it first and then the
1225 * iflist_mtx.
1226 */
1227 mutex_lock_nested(&sdata->u.mgd.mtx, SINGLE_DEPTH_NESTING);
1228 count += check_mgd_smps(&sdata->u.mgd, &smps_mode);
1229 mutex_unlock(&sdata->u.mgd.mtx);
1230 } else
1231 count += check_mgd_smps(&sdata->u.mgd, &smps_mode);
1232
1233 if (count > 1) {
1234 smps_mode = IEEE80211_SMPS_OFF;
1235 break;
1236 }
1237 }
1238
1239 if (smps_mode == local->smps_mode)
1240 return;
1241
1242 set:
1243 local->smps_mode = smps_mode;
1244 /* changed flag is auto-detected for this */
1245 ieee80211_hw_config(local, 0);
1246}