diff options
Diffstat (limited to 'net/mac80211')
-rw-r--r-- | net/mac80211/cfg.c | 49 | ||||
-rw-r--r-- | net/mac80211/debugfs_netdev.c | 111 | ||||
-rw-r--r-- | net/mac80211/driver-trace.h | 2 | ||||
-rw-r--r-- | net/mac80211/ht.c | 47 | ||||
-rw-r--r-- | net/mac80211/ieee80211_i.h | 14 | ||||
-rw-r--r-- | net/mac80211/main.c | 24 | ||||
-rw-r--r-- | net/mac80211/mlme.c | 63 | ||||
-rw-r--r-- | net/mac80211/status.c | 38 | ||||
-rw-r--r-- | net/mac80211/util.c | 74 |
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 | ||
1321 | int __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 | |||
1321 | static int ieee80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev, | 1365 | static 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 | ||
44 | static 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) \ |
45 | static ssize_t ieee80211_if_fmt_##name( \ | 69 | static 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) \ |
75 | static ssize_t ieee80211_if_read_##name(struct file *file, \ | 99 | static 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 | } \ |
83 | static const struct file_operations name##_ops = { \ | 107 | static 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) \ | ||
114 | static 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 */ |
93 | IEEE80211_IF_FILE(drop_unencrypted, drop_unencrypted, DEC); | 129 | IEEE80211_IF_FILE(drop_unencrypted, drop_unencrypted, DEC); |
@@ -99,6 +135,70 @@ IEEE80211_IF_FILE(bssid, u.mgd.bssid, MAC); | |||
99 | IEEE80211_IF_FILE(aid, u.mgd.aid, DEC); | 135 | IEEE80211_IF_FILE(aid, u.mgd.aid, DEC); |
100 | IEEE80211_IF_FILE(capab, u.mgd.capab, HEX); | 136 | IEEE80211_IF_FILE(capab, u.mgd.capab, HEX); |
101 | 137 | ||
138 | static 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 | |||
165 | static 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 | |||
172 | static 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 | |||
183 | static 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 */ |
103 | IEEE80211_IF_FILE(num_sta_ps, u.ap.num_sta_ps, ATOMIC); | 203 | IEEE80211_IF_FILE(num_sta_ps, u.ap.num_sta_ps, ATOMIC); |
104 | IEEE80211_IF_FILE(dtim_count, u.ap.dtim_count, DEC); | 204 | IEEE80211_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 */ |
115 | IEEE80211_IF_FILE(peer, u.wds.remote_addr, MAC); | 215 | IEEE80211_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 | |||
161 | static void add_sta_files(struct ieee80211_sub_if_data *sdata) | 265 | static 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 | ||
172 | static void add_ap_files(struct ieee80211_sub_if_data *sdata) | 277 | static 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 | |||
170 | int 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 | |||
978 | void ieee80211_send_delba(struct ieee80211_sub_if_data *sdata, | 985 | void 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); |
988 | int 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 | ||
982 | void ieee80211_sta_stop_rx_ba_session(struct ieee80211_sub_if_data *sdata, u8 *da, | 992 | void 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, | |||
1088 | u32 ieee80211_sta_get_rates(struct ieee80211_local *local, | 1098 | u32 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); |
1101 | int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata, | ||
1102 | enum ieee80211_smps_mode smps_mode); | ||
1103 | void 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 | } |
298 | EXPORT_SYMBOL(ieee80211_restart_hw); | 310 | EXPORT_SYMBOL(ieee80211_restart_hw); |
299 | 311 | ||
312 | static 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 | |||
300 | struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, | 322 | struct 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 | ||
137 | static 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 | |||
137 | void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) | 171 | void 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 | ||
1173 | static 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 */ | ||
1193 | void 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 | } | ||