aboutsummaryrefslogtreecommitdiffstats
path: root/net/mac80211
diff options
context:
space:
mode:
Diffstat (limited to 'net/mac80211')
-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
9 files changed, 415 insertions, 7 deletions
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}