aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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}