diff options
-rw-r--r-- | include/linux/ieee80211.h | 8 | ||||
-rw-r--r-- | net/mac80211/Kconfig | 11 | ||||
-rw-r--r-- | net/mac80211/Makefile | 3 | ||||
-rw-r--r-- | net/mac80211/cfg.c | 27 | ||||
-rw-r--r-- | net/mac80211/debug.h | 10 | ||||
-rw-r--r-- | net/mac80211/debugfs_netdev.c | 5 | ||||
-rw-r--r-- | net/mac80211/debugfs_sta.c | 5 | ||||
-rw-r--r-- | net/mac80211/ieee80211_i.h | 6 | ||||
-rw-r--r-- | net/mac80211/mesh.c | 33 | ||||
-rw-r--r-- | net/mac80211/mesh.h | 17 | ||||
-rw-r--r-- | net/mac80211/mesh_hwmp.c | 7 | ||||
-rw-r--r-- | net/mac80211/mesh_pathtbl.c | 1 | ||||
-rw-r--r-- | net/mac80211/mesh_plink.c | 17 | ||||
-rw-r--r-- | net/mac80211/mesh_ps.c | 585 | ||||
-rw-r--r-- | net/mac80211/rx.c | 7 | ||||
-rw-r--r-- | net/mac80211/sta_info.c | 20 | ||||
-rw-r--r-- | net/mac80211/sta_info.h | 11 | ||||
-rw-r--r-- | net/mac80211/status.c | 7 | ||||
-rw-r--r-- | net/mac80211/tx.c | 31 | ||||
-rw-r--r-- | net/mac80211/util.c | 4 | ||||
-rw-r--r-- | net/mac80211/wme.c | 13 |
21 files changed, 811 insertions, 17 deletions
diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 11c8bc87fdcb..7e8a498efe6d 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h | |||
@@ -151,6 +151,11 @@ | |||
151 | /* Mesh Control 802.11s */ | 151 | /* Mesh Control 802.11s */ |
152 | #define IEEE80211_QOS_CTL_MESH_CONTROL_PRESENT 0x0100 | 152 | #define IEEE80211_QOS_CTL_MESH_CONTROL_PRESENT 0x0100 |
153 | 153 | ||
154 | /* Mesh Power Save Level */ | ||
155 | #define IEEE80211_QOS_CTL_MESH_PS_LEVEL 0x0200 | ||
156 | /* Mesh Receiver Service Period Initiated */ | ||
157 | #define IEEE80211_QOS_CTL_RSPI 0x0400 | ||
158 | |||
154 | /* U-APSD queue for WMM IEs sent by AP */ | 159 | /* U-APSD queue for WMM IEs sent by AP */ |
155 | #define IEEE80211_WMM_IE_AP_QOSINFO_UAPSD (1<<7) | 160 | #define IEEE80211_WMM_IE_AP_QOSINFO_UAPSD (1<<7) |
156 | #define IEEE80211_WMM_IE_AP_QOSINFO_PARAM_SET_CNT_MASK 0x0f | 161 | #define IEEE80211_WMM_IE_AP_QOSINFO_PARAM_SET_CNT_MASK 0x0f |
@@ -675,11 +680,14 @@ struct ieee80211_meshconf_ie { | |||
675 | * @IEEE80211_MESHCONF_CAPAB_FORWARDING: the STA forwards MSDUs | 680 | * @IEEE80211_MESHCONF_CAPAB_FORWARDING: the STA forwards MSDUs |
676 | * @IEEE80211_MESHCONF_CAPAB_TBTT_ADJUSTING: TBTT adjustment procedure | 681 | * @IEEE80211_MESHCONF_CAPAB_TBTT_ADJUSTING: TBTT adjustment procedure |
677 | * is ongoing | 682 | * is ongoing |
683 | * @IEEE80211_MESHCONF_CAPAB_POWER_SAVE_LEVEL: STA is in deep sleep mode or has | ||
684 | * neighbors in deep sleep mode | ||
678 | */ | 685 | */ |
679 | enum mesh_config_capab_flags { | 686 | enum mesh_config_capab_flags { |
680 | IEEE80211_MESHCONF_CAPAB_ACCEPT_PLINKS = 0x01, | 687 | IEEE80211_MESHCONF_CAPAB_ACCEPT_PLINKS = 0x01, |
681 | IEEE80211_MESHCONF_CAPAB_FORWARDING = 0x08, | 688 | IEEE80211_MESHCONF_CAPAB_FORWARDING = 0x08, |
682 | IEEE80211_MESHCONF_CAPAB_TBTT_ADJUSTING = 0x20, | 689 | IEEE80211_MESHCONF_CAPAB_TBTT_ADJUSTING = 0x20, |
690 | IEEE80211_MESHCONF_CAPAB_POWER_SAVE_LEVEL = 0x40, | ||
683 | }; | 691 | }; |
684 | 692 | ||
685 | /** | 693 | /** |
diff --git a/net/mac80211/Kconfig b/net/mac80211/Kconfig index b4ecf267a34b..0ecf947ad378 100644 --- a/net/mac80211/Kconfig +++ b/net/mac80211/Kconfig | |||
@@ -258,6 +258,17 @@ config MAC80211_MESH_SYNC_DEBUG | |||
258 | 258 | ||
259 | Do not select this option. | 259 | Do not select this option. |
260 | 260 | ||
261 | config MAC80211_MESH_PS_DEBUG | ||
262 | bool "Verbose mesh powersave debugging" | ||
263 | depends on MAC80211_DEBUG_MENU | ||
264 | depends on MAC80211_MESH | ||
265 | ---help--- | ||
266 | Selecting this option causes mac80211 to print out very verbose mesh | ||
267 | powersave debugging messages (when mac80211 is taking part in a | ||
268 | mesh network). | ||
269 | |||
270 | Do not select this option. | ||
271 | |||
261 | config MAC80211_TDLS_DEBUG | 272 | config MAC80211_TDLS_DEBUG |
262 | bool "Verbose TDLS debugging" | 273 | bool "Verbose TDLS debugging" |
263 | depends on MAC80211_DEBUG_MENU | 274 | depends on MAC80211_DEBUG_MENU |
diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile index 4911202334d9..9d7d840aac6d 100644 --- a/net/mac80211/Makefile +++ b/net/mac80211/Makefile | |||
@@ -39,7 +39,8 @@ mac80211-$(CONFIG_MAC80211_MESH) += \ | |||
39 | mesh_pathtbl.o \ | 39 | mesh_pathtbl.o \ |
40 | mesh_plink.o \ | 40 | mesh_plink.o \ |
41 | mesh_hwmp.o \ | 41 | mesh_hwmp.o \ |
42 | mesh_sync.o | 42 | mesh_sync.o \ |
43 | mesh_ps.o | ||
43 | 44 | ||
44 | mac80211-$(CONFIG_PM) += pm.o | 45 | mac80211-$(CONFIG_PM) += pm.o |
45 | 46 | ||
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 661b878bd19c..f4f7e7691077 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c | |||
@@ -492,7 +492,10 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) | |||
492 | #ifdef CONFIG_MAC80211_MESH | 492 | #ifdef CONFIG_MAC80211_MESH |
493 | sinfo->filled |= STATION_INFO_LLID | | 493 | sinfo->filled |= STATION_INFO_LLID | |
494 | STATION_INFO_PLID | | 494 | STATION_INFO_PLID | |
495 | STATION_INFO_PLINK_STATE; | 495 | STATION_INFO_PLINK_STATE | |
496 | STATION_INFO_LOCAL_PM | | ||
497 | STATION_INFO_PEER_PM | | ||
498 | STATION_INFO_NONPEER_PM; | ||
496 | 499 | ||
497 | sinfo->llid = le16_to_cpu(sta->llid); | 500 | sinfo->llid = le16_to_cpu(sta->llid); |
498 | sinfo->plid = le16_to_cpu(sta->plid); | 501 | sinfo->plid = le16_to_cpu(sta->plid); |
@@ -501,6 +504,9 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) | |||
501 | sinfo->filled |= STATION_INFO_T_OFFSET; | 504 | sinfo->filled |= STATION_INFO_T_OFFSET; |
502 | sinfo->t_offset = sta->t_offset; | 505 | sinfo->t_offset = sta->t_offset; |
503 | } | 506 | } |
507 | sinfo->local_pm = sta->local_pm; | ||
508 | sinfo->peer_pm = sta->peer_pm; | ||
509 | sinfo->nonpeer_pm = sta->nonpeer_pm; | ||
504 | #endif | 510 | #endif |
505 | } | 511 | } |
506 | 512 | ||
@@ -1262,6 +1268,10 @@ static int sta_apply_parameters(struct ieee80211_local *local, | |||
1262 | changed = mesh_plink_inc_estab_count( | 1268 | changed = mesh_plink_inc_estab_count( |
1263 | sdata); | 1269 | sdata); |
1264 | sta->plink_state = params->plink_state; | 1270 | sta->plink_state = params->plink_state; |
1271 | |||
1272 | ieee80211_mps_sta_status_update(sta); | ||
1273 | ieee80211_mps_set_sta_local_pm(sta, | ||
1274 | sdata->u.mesh.mshcfg.power_mode); | ||
1265 | break; | 1275 | break; |
1266 | case NL80211_PLINK_LISTEN: | 1276 | case NL80211_PLINK_LISTEN: |
1267 | case NL80211_PLINK_BLOCKED: | 1277 | case NL80211_PLINK_BLOCKED: |
@@ -1273,6 +1283,9 @@ static int sta_apply_parameters(struct ieee80211_local *local, | |||
1273 | changed = mesh_plink_dec_estab_count( | 1283 | changed = mesh_plink_dec_estab_count( |
1274 | sdata); | 1284 | sdata); |
1275 | sta->plink_state = params->plink_state; | 1285 | sta->plink_state = params->plink_state; |
1286 | |||
1287 | ieee80211_mps_sta_status_update(sta); | ||
1288 | ieee80211_mps_local_status_update(sdata); | ||
1276 | break; | 1289 | break; |
1277 | default: | 1290 | default: |
1278 | /* nothing */ | 1291 | /* nothing */ |
@@ -1289,6 +1302,9 @@ static int sta_apply_parameters(struct ieee80211_local *local, | |||
1289 | break; | 1302 | break; |
1290 | } | 1303 | } |
1291 | } | 1304 | } |
1305 | |||
1306 | if (params->local_pm) | ||
1307 | ieee80211_mps_set_sta_local_pm(sta, params->local_pm); | ||
1292 | #endif | 1308 | #endif |
1293 | } | 1309 | } |
1294 | 1310 | ||
@@ -1777,6 +1793,15 @@ static int ieee80211_update_mesh_config(struct wiphy *wiphy, | |||
1777 | if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL, mask)) | 1793 | if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL, mask)) |
1778 | conf->dot11MeshHWMPconfirmationInterval = | 1794 | conf->dot11MeshHWMPconfirmationInterval = |
1779 | nconf->dot11MeshHWMPconfirmationInterval; | 1795 | nconf->dot11MeshHWMPconfirmationInterval; |
1796 | if (_chg_mesh_attr(NL80211_MESHCONF_POWER_MODE, mask)) { | ||
1797 | conf->power_mode = nconf->power_mode; | ||
1798 | ieee80211_mps_local_status_update(sdata); | ||
1799 | } | ||
1800 | if (_chg_mesh_attr(NL80211_MESHCONF_AWAKE_WINDOW, mask)) { | ||
1801 | conf->dot11MeshAwakeWindowDuration = | ||
1802 | nconf->dot11MeshAwakeWindowDuration; | ||
1803 | ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON); | ||
1804 | } | ||
1780 | return 0; | 1805 | return 0; |
1781 | } | 1806 | } |
1782 | 1807 | ||
diff --git a/net/mac80211/debug.h b/net/mac80211/debug.h index 8f383a576016..4ccc5ed6237d 100644 --- a/net/mac80211/debug.h +++ b/net/mac80211/debug.h | |||
@@ -44,6 +44,12 @@ | |||
44 | #define MAC80211_MESH_SYNC_DEBUG 0 | 44 | #define MAC80211_MESH_SYNC_DEBUG 0 |
45 | #endif | 45 | #endif |
46 | 46 | ||
47 | #ifdef CONFIG_MAC80211_MESH_PS_DEBUG | ||
48 | #define MAC80211_MESH_PS_DEBUG 1 | ||
49 | #else | ||
50 | #define MAC80211_MESH_PS_DEBUG 0 | ||
51 | #endif | ||
52 | |||
47 | #ifdef CONFIG_MAC80211_TDLS_DEBUG | 53 | #ifdef CONFIG_MAC80211_TDLS_DEBUG |
48 | #define MAC80211_TDLS_DEBUG 1 | 54 | #define MAC80211_TDLS_DEBUG 1 |
49 | #else | 55 | #else |
@@ -151,6 +157,10 @@ do { \ | |||
151 | _sdata_dbg(MAC80211_MESH_SYNC_DEBUG, \ | 157 | _sdata_dbg(MAC80211_MESH_SYNC_DEBUG, \ |
152 | sdata, fmt, ##__VA_ARGS__) | 158 | sdata, fmt, ##__VA_ARGS__) |
153 | 159 | ||
160 | #define mps_dbg(sdata, fmt, ...) \ | ||
161 | _sdata_dbg(MAC80211_MESH_PS_DEBUG, \ | ||
162 | sdata, fmt, ##__VA_ARGS__) | ||
163 | |||
154 | #define tdls_dbg(sdata, fmt, ...) \ | 164 | #define tdls_dbg(sdata, fmt, ...) \ |
155 | _sdata_dbg(MAC80211_TDLS_DEBUG, \ | 165 | _sdata_dbg(MAC80211_TDLS_DEBUG, \ |
156 | sdata, fmt, ##__VA_ARGS__) | 166 | sdata, fmt, ##__VA_ARGS__) |
diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c index cbde5cc49a40..059bbb82e84f 100644 --- a/net/mac80211/debugfs_netdev.c +++ b/net/mac80211/debugfs_netdev.c | |||
@@ -515,6 +515,9 @@ IEEE80211_IF_FILE(dot11MeshHWMProotInterval, | |||
515 | u.mesh.mshcfg.dot11MeshHWMProotInterval, DEC); | 515 | u.mesh.mshcfg.dot11MeshHWMProotInterval, DEC); |
516 | IEEE80211_IF_FILE(dot11MeshHWMPconfirmationInterval, | 516 | IEEE80211_IF_FILE(dot11MeshHWMPconfirmationInterval, |
517 | u.mesh.mshcfg.dot11MeshHWMPconfirmationInterval, DEC); | 517 | u.mesh.mshcfg.dot11MeshHWMPconfirmationInterval, DEC); |
518 | IEEE80211_IF_FILE(power_mode, u.mesh.mshcfg.power_mode, DEC); | ||
519 | IEEE80211_IF_FILE(dot11MeshAwakeWindowDuration, | ||
520 | u.mesh.mshcfg.dot11MeshAwakeWindowDuration, DEC); | ||
518 | #endif | 521 | #endif |
519 | 522 | ||
520 | #define DEBUGFS_ADD_MODE(name, mode) \ | 523 | #define DEBUGFS_ADD_MODE(name, mode) \ |
@@ -620,6 +623,8 @@ static void add_mesh_config(struct ieee80211_sub_if_data *sdata) | |||
620 | MESHPARAMS_ADD(dot11MeshHWMPactivePathToRootTimeout); | 623 | MESHPARAMS_ADD(dot11MeshHWMPactivePathToRootTimeout); |
621 | MESHPARAMS_ADD(dot11MeshHWMProotInterval); | 624 | MESHPARAMS_ADD(dot11MeshHWMProotInterval); |
622 | MESHPARAMS_ADD(dot11MeshHWMPconfirmationInterval); | 625 | MESHPARAMS_ADD(dot11MeshHWMPconfirmationInterval); |
626 | MESHPARAMS_ADD(power_mode); | ||
627 | MESHPARAMS_ADD(dot11MeshAwakeWindowDuration); | ||
623 | #undef MESHPARAMS_ADD | 628 | #undef MESHPARAMS_ADD |
624 | } | 629 | } |
625 | #endif | 630 | #endif |
diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c index 6fb1168b9f16..c7591f73dbc3 100644 --- a/net/mac80211/debugfs_sta.c +++ b/net/mac80211/debugfs_sta.c | |||
@@ -65,7 +65,7 @@ static ssize_t sta_flags_read(struct file *file, char __user *userbuf, | |||
65 | test_sta_flag(sta, WLAN_STA_##flg) ? #flg "\n" : "" | 65 | test_sta_flag(sta, WLAN_STA_##flg) ? #flg "\n" : "" |
66 | 66 | ||
67 | int res = scnprintf(buf, sizeof(buf), | 67 | int res = scnprintf(buf, sizeof(buf), |
68 | "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", | 68 | "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", |
69 | TEST(AUTH), TEST(ASSOC), TEST(PS_STA), | 69 | TEST(AUTH), TEST(ASSOC), TEST(PS_STA), |
70 | TEST(PS_DRIVER), TEST(AUTHORIZED), | 70 | TEST(PS_DRIVER), TEST(AUTHORIZED), |
71 | TEST(SHORT_PREAMBLE), | 71 | TEST(SHORT_PREAMBLE), |
@@ -74,7 +74,8 @@ static ssize_t sta_flags_read(struct file *file, char __user *userbuf, | |||
74 | TEST(UAPSD), TEST(SP), TEST(TDLS_PEER), | 74 | TEST(UAPSD), TEST(SP), TEST(TDLS_PEER), |
75 | TEST(TDLS_PEER_AUTH), TEST(4ADDR_EVENT), | 75 | TEST(TDLS_PEER_AUTH), TEST(4ADDR_EVENT), |
76 | TEST(INSERTED), TEST(RATE_CONTROL), | 76 | TEST(INSERTED), TEST(RATE_CONTROL), |
77 | TEST(TOFFSET_KNOWN)); | 77 | TEST(TOFFSET_KNOWN), TEST(MPSP_OWNER), |
78 | TEST(MPSP_RECIPIENT)); | ||
78 | #undef TEST | 79 | #undef TEST |
79 | return simple_read_from_buffer(userbuf, count, ppos, buf, res); | 80 | return simple_read_from_buffer(userbuf, count, ppos, buf, res); |
80 | } | 81 | } |
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 8faf360e0b4c..5fe9db707880 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h | |||
@@ -590,6 +590,11 @@ struct ieee80211_if_mesh { | |||
590 | s64 sync_offset_clockdrift_max; | 590 | s64 sync_offset_clockdrift_max; |
591 | spinlock_t sync_offset_lock; | 591 | spinlock_t sync_offset_lock; |
592 | bool adjusting_tbtt; | 592 | bool adjusting_tbtt; |
593 | /* mesh power save */ | ||
594 | enum nl80211_mesh_power_mode nonpeer_pm; | ||
595 | int ps_peers_light_sleep; | ||
596 | int ps_peers_deep_sleep; | ||
597 | struct ps_data ps; | ||
593 | }; | 598 | }; |
594 | 599 | ||
595 | #ifdef CONFIG_MAC80211_MESH | 600 | #ifdef CONFIG_MAC80211_MESH |
@@ -1185,6 +1190,7 @@ struct ieee802_11_elems { | |||
1185 | struct ieee80211_meshconf_ie *mesh_config; | 1190 | struct ieee80211_meshconf_ie *mesh_config; |
1186 | u8 *mesh_id; | 1191 | u8 *mesh_id; |
1187 | u8 *peering; | 1192 | u8 *peering; |
1193 | __le16 *awake_window; | ||
1188 | u8 *preq; | 1194 | u8 *preq; |
1189 | u8 *prep; | 1195 | u8 *prep; |
1190 | u8 *perr; | 1196 | u8 *perr; |
diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index f920da1201ab..35ac38871420 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c | |||
@@ -261,6 +261,9 @@ mesh_add_meshconf_ie(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata) | |||
261 | *pos = IEEE80211_MESHCONF_CAPAB_FORWARDING; | 261 | *pos = IEEE80211_MESHCONF_CAPAB_FORWARDING; |
262 | *pos |= ifmsh->accepting_plinks ? | 262 | *pos |= ifmsh->accepting_plinks ? |
263 | IEEE80211_MESHCONF_CAPAB_ACCEPT_PLINKS : 0x00; | 263 | IEEE80211_MESHCONF_CAPAB_ACCEPT_PLINKS : 0x00; |
264 | /* Mesh PS mode. See IEEE802.11-2012 8.4.2.100.8 */ | ||
265 | *pos |= ifmsh->ps_peers_deep_sleep ? | ||
266 | IEEE80211_MESHCONF_CAPAB_POWER_SAVE_LEVEL : 0x00; | ||
264 | *pos++ |= ifmsh->adjusting_tbtt ? | 267 | *pos++ |= ifmsh->adjusting_tbtt ? |
265 | IEEE80211_MESHCONF_CAPAB_TBTT_ADJUSTING : 0x00; | 268 | IEEE80211_MESHCONF_CAPAB_TBTT_ADJUSTING : 0x00; |
266 | *pos++ = 0x00; | 269 | *pos++ = 0x00; |
@@ -286,6 +289,29 @@ mesh_add_meshid_ie(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata) | |||
286 | return 0; | 289 | return 0; |
287 | } | 290 | } |
288 | 291 | ||
292 | int mesh_add_awake_window_ie(struct sk_buff *skb, | ||
293 | struct ieee80211_sub_if_data *sdata) | ||
294 | { | ||
295 | struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; | ||
296 | u8 *pos; | ||
297 | |||
298 | /* see IEEE802.11-2012 13.14.6 */ | ||
299 | if (ifmsh->ps_peers_light_sleep == 0 && | ||
300 | ifmsh->ps_peers_deep_sleep == 0 && | ||
301 | ifmsh->nonpeer_pm == NL80211_MESH_POWER_ACTIVE) | ||
302 | return 0; | ||
303 | |||
304 | if (skb_tailroom(skb) < 4) | ||
305 | return -ENOMEM; | ||
306 | |||
307 | pos = skb_put(skb, 2 + 2); | ||
308 | *pos++ = WLAN_EID_MESH_AWAKE_WINDOW; | ||
309 | *pos++ = 2; | ||
310 | put_unaligned_le16(ifmsh->mshcfg.dot11MeshAwakeWindowDuration, pos); | ||
311 | |||
312 | return 0; | ||
313 | } | ||
314 | |||
289 | int | 315 | int |
290 | mesh_add_vendor_ies(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata) | 316 | mesh_add_vendor_ies(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata) |
291 | { | 317 | { |
@@ -629,6 +655,8 @@ void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata) | |||
629 | sdata->vif.bss_conf.basic_rates = | 655 | sdata->vif.bss_conf.basic_rates = |
630 | ieee80211_mandatory_rates(local, band); | 656 | ieee80211_mandatory_rates(local, band); |
631 | 657 | ||
658 | ieee80211_mps_local_status_update(sdata); | ||
659 | |||
632 | ieee80211_bss_info_change_notify(sdata, changed); | 660 | ieee80211_bss_info_change_notify(sdata, changed); |
633 | 661 | ||
634 | netif_carrier_on(sdata->dev); | 662 | netif_carrier_on(sdata->dev); |
@@ -651,6 +679,10 @@ void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata) | |||
651 | sta_info_flush(sdata); | 679 | sta_info_flush(sdata); |
652 | mesh_path_flush_by_iface(sdata); | 680 | mesh_path_flush_by_iface(sdata); |
653 | 681 | ||
682 | /* free all potentially still buffered group-addressed frames */ | ||
683 | local->total_ps_buffered -= skb_queue_len(&ifmsh->ps.bc_buf); | ||
684 | skb_queue_purge(&ifmsh->ps.bc_buf); | ||
685 | |||
654 | del_timer_sync(&sdata->u.mesh.housekeeping_timer); | 686 | del_timer_sync(&sdata->u.mesh.housekeeping_timer); |
655 | del_timer_sync(&sdata->u.mesh.mesh_path_root_timer); | 687 | del_timer_sync(&sdata->u.mesh.mesh_path_root_timer); |
656 | del_timer_sync(&sdata->u.mesh.mesh_path_timer); | 688 | del_timer_sync(&sdata->u.mesh.mesh_path_timer); |
@@ -828,6 +860,7 @@ void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata) | |||
828 | ieee80211_mesh_path_root_timer, | 860 | ieee80211_mesh_path_root_timer, |
829 | (unsigned long) sdata); | 861 | (unsigned long) sdata); |
830 | INIT_LIST_HEAD(&ifmsh->preq_queue.list); | 862 | INIT_LIST_HEAD(&ifmsh->preq_queue.list); |
863 | skb_queue_head_init(&ifmsh->ps.bc_buf); | ||
831 | spin_lock_init(&ifmsh->mesh_preq_queue_lock); | 864 | spin_lock_init(&ifmsh->mesh_preq_queue_lock); |
832 | spin_lock_init(&ifmsh->sync_offset_lock); | 865 | spin_lock_init(&ifmsh->sync_offset_lock); |
833 | 866 | ||
diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h index aff301544c7f..eb336253b6b3 100644 --- a/net/mac80211/mesh.h +++ b/net/mac80211/mesh.h | |||
@@ -222,6 +222,8 @@ int mesh_add_meshid_ie(struct sk_buff *skb, | |||
222 | struct ieee80211_sub_if_data *sdata); | 222 | struct ieee80211_sub_if_data *sdata); |
223 | int mesh_add_rsn_ie(struct sk_buff *skb, | 223 | int mesh_add_rsn_ie(struct sk_buff *skb, |
224 | struct ieee80211_sub_if_data *sdata); | 224 | struct ieee80211_sub_if_data *sdata); |
225 | int mesh_add_awake_window_ie(struct sk_buff *skb, | ||
226 | struct ieee80211_sub_if_data *sdata); | ||
225 | int mesh_add_vendor_ies(struct sk_buff *skb, | 227 | int mesh_add_vendor_ies(struct sk_buff *skb, |
226 | struct ieee80211_sub_if_data *sdata); | 228 | struct ieee80211_sub_if_data *sdata); |
227 | int mesh_add_ds_params_ie(struct sk_buff *skb, | 229 | int mesh_add_ds_params_ie(struct sk_buff *skb, |
@@ -242,6 +244,21 @@ void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata); | |||
242 | void ieee80211_mesh_root_setup(struct ieee80211_if_mesh *ifmsh); | 244 | void ieee80211_mesh_root_setup(struct ieee80211_if_mesh *ifmsh); |
243 | const struct ieee80211_mesh_sync_ops *ieee80211_mesh_sync_ops_get(u8 method); | 245 | const struct ieee80211_mesh_sync_ops *ieee80211_mesh_sync_ops_get(u8 method); |
244 | 246 | ||
247 | /* mesh power save */ | ||
248 | void ieee80211_mps_local_status_update(struct ieee80211_sub_if_data *sdata); | ||
249 | void ieee80211_mps_set_sta_local_pm(struct sta_info *sta, | ||
250 | enum nl80211_mesh_power_mode pm); | ||
251 | void ieee80211_mps_set_frame_flags(struct ieee80211_sub_if_data *sdata, | ||
252 | struct sta_info *sta, | ||
253 | struct ieee80211_hdr *hdr); | ||
254 | void ieee80211_mps_sta_status_update(struct sta_info *sta); | ||
255 | void ieee80211_mps_rx_h_sta_process(struct sta_info *sta, | ||
256 | struct ieee80211_hdr *hdr); | ||
257 | void ieee80211_mpsp_trigger_process(u8 *qc, struct sta_info *sta, | ||
258 | bool tx, bool acked); | ||
259 | void ieee80211_mps_frame_release(struct sta_info *sta, | ||
260 | struct ieee802_11_elems *elems); | ||
261 | |||
245 | /* Mesh paths */ | 262 | /* Mesh paths */ |
246 | int mesh_nexthop_lookup(struct sk_buff *skb, | 263 | int mesh_nexthop_lookup(struct sk_buff *skb, |
247 | struct ieee80211_sub_if_data *sdata); | 264 | struct ieee80211_sub_if_data *sdata); |
diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c index 6b4603a90031..f0dd8742ed42 100644 --- a/net/mac80211/mesh_hwmp.c +++ b/net/mac80211/mesh_hwmp.c | |||
@@ -205,6 +205,7 @@ static void prepare_frame_for_deferred_tx(struct ieee80211_sub_if_data *sdata, | |||
205 | struct sk_buff *skb) | 205 | struct sk_buff *skb) |
206 | { | 206 | { |
207 | struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); | 207 | struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); |
208 | struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; | ||
208 | 209 | ||
209 | skb_set_mac_header(skb, 0); | 210 | skb_set_mac_header(skb, 0); |
210 | skb_set_network_header(skb, 0); | 211 | skb_set_network_header(skb, 0); |
@@ -217,6 +218,7 @@ static void prepare_frame_for_deferred_tx(struct ieee80211_sub_if_data *sdata, | |||
217 | info->control.vif = &sdata->vif; | 218 | info->control.vif = &sdata->vif; |
218 | info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING; | 219 | info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING; |
219 | ieee80211_set_qos_hdr(sdata, skb); | 220 | ieee80211_set_qos_hdr(sdata, skb); |
221 | ieee80211_mps_set_frame_flags(sdata, NULL, hdr); | ||
220 | } | 222 | } |
221 | 223 | ||
222 | /** | 224 | /** |
@@ -1080,6 +1082,10 @@ int mesh_nexthop_resolve(struct sk_buff *skb, | |||
1080 | u8 *target_addr = hdr->addr3; | 1082 | u8 *target_addr = hdr->addr3; |
1081 | int err = 0; | 1083 | int err = 0; |
1082 | 1084 | ||
1085 | /* Nulls are only sent to peers for PS and should be pre-addressed */ | ||
1086 | if (ieee80211_is_qos_nullfunc(hdr->frame_control)) | ||
1087 | return 0; | ||
1088 | |||
1083 | rcu_read_lock(); | 1089 | rcu_read_lock(); |
1084 | err = mesh_nexthop_lookup(skb, sdata); | 1090 | err = mesh_nexthop_lookup(skb, sdata); |
1085 | if (!err) | 1091 | if (!err) |
@@ -1151,6 +1157,7 @@ int mesh_nexthop_lookup(struct sk_buff *skb, | |||
1151 | if (next_hop) { | 1157 | if (next_hop) { |
1152 | memcpy(hdr->addr1, next_hop->sta.addr, ETH_ALEN); | 1158 | memcpy(hdr->addr1, next_hop->sta.addr, ETH_ALEN); |
1153 | memcpy(hdr->addr2, sdata->vif.addr, ETH_ALEN); | 1159 | memcpy(hdr->addr2, sdata->vif.addr, ETH_ALEN); |
1160 | ieee80211_mps_set_frame_flags(sdata, next_hop, hdr); | ||
1154 | err = 0; | 1161 | err = 0; |
1155 | } | 1162 | } |
1156 | 1163 | ||
diff --git a/net/mac80211/mesh_pathtbl.c b/net/mac80211/mesh_pathtbl.c index aa749818860e..d5786c3eaee2 100644 --- a/net/mac80211/mesh_pathtbl.c +++ b/net/mac80211/mesh_pathtbl.c | |||
@@ -212,6 +212,7 @@ void mesh_path_assign_nexthop(struct mesh_path *mpath, struct sta_info *sta) | |||
212 | hdr = (struct ieee80211_hdr *) skb->data; | 212 | hdr = (struct ieee80211_hdr *) skb->data; |
213 | memcpy(hdr->addr1, sta->sta.addr, ETH_ALEN); | 213 | memcpy(hdr->addr1, sta->sta.addr, ETH_ALEN); |
214 | memcpy(hdr->addr2, mpath->sdata->vif.addr, ETH_ALEN); | 214 | memcpy(hdr->addr2, mpath->sdata->vif.addr, ETH_ALEN); |
215 | ieee80211_mps_set_frame_flags(sta->sdata, sta, hdr); | ||
215 | } | 216 | } |
216 | 217 | ||
217 | spin_unlock_irqrestore(&mpath->frame_queue.lock, flags); | 218 | spin_unlock_irqrestore(&mpath->frame_queue.lock, flags); |
diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index 6787d696d94c..fe7c3334d6fe 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c | |||
@@ -201,6 +201,9 @@ static u32 __mesh_plink_deactivate(struct sta_info *sta) | |||
201 | sta->plink_state = NL80211_PLINK_BLOCKED; | 201 | sta->plink_state = NL80211_PLINK_BLOCKED; |
202 | mesh_path_flush_by_nexthop(sta); | 202 | mesh_path_flush_by_nexthop(sta); |
203 | 203 | ||
204 | ieee80211_mps_sta_status_update(sta); | ||
205 | ieee80211_mps_local_status_update(sdata); | ||
206 | |||
204 | return changed; | 207 | return changed; |
205 | } | 208 | } |
206 | 209 | ||
@@ -503,6 +506,7 @@ void mesh_neighbour_update(struct ieee80211_sub_if_data *sdata, | |||
503 | rssi_threshold_check(sta, sdata)) | 506 | rssi_threshold_check(sta, sdata)) |
504 | mesh_plink_open(sta); | 507 | mesh_plink_open(sta); |
505 | 508 | ||
509 | ieee80211_mps_frame_release(sta, elems); | ||
506 | out: | 510 | out: |
507 | rcu_read_unlock(); | 511 | rcu_read_unlock(); |
508 | } | 512 | } |
@@ -633,6 +637,9 @@ int mesh_plink_open(struct sta_info *sta) | |||
633 | "Mesh plink: starting establishment with %pM\n", | 637 | "Mesh plink: starting establishment with %pM\n", |
634 | sta->sta.addr); | 638 | sta->sta.addr); |
635 | 639 | ||
640 | /* set the non-peer mode to active during peering */ | ||
641 | ieee80211_mps_local_status_update(sdata); | ||
642 | |||
636 | return mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_OPEN, | 643 | return mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_OPEN, |
637 | sta->sta.addr, llid, 0, 0); | 644 | sta->sta.addr, llid, 0, 0); |
638 | } | 645 | } |
@@ -866,6 +873,10 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m | |||
866 | sta->llid = llid; | 873 | sta->llid = llid; |
867 | mesh_plink_timer_set(sta, | 874 | mesh_plink_timer_set(sta, |
868 | mshcfg->dot11MeshRetryTimeout); | 875 | mshcfg->dot11MeshRetryTimeout); |
876 | |||
877 | /* set the non-peer mode to active during peering */ | ||
878 | ieee80211_mps_local_status_update(sdata); | ||
879 | |||
869 | spin_unlock_bh(&sta->lock); | 880 | spin_unlock_bh(&sta->lock); |
870 | mesh_plink_frame_tx(sdata, | 881 | mesh_plink_frame_tx(sdata, |
871 | WLAN_SP_MESH_PEERING_OPEN, | 882 | WLAN_SP_MESH_PEERING_OPEN, |
@@ -959,6 +970,9 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m | |||
959 | changed |= mesh_set_short_slot_time(sdata); | 970 | changed |= mesh_set_short_slot_time(sdata); |
960 | mpl_dbg(sdata, "Mesh plink with %pM ESTABLISHED\n", | 971 | mpl_dbg(sdata, "Mesh plink with %pM ESTABLISHED\n", |
961 | sta->sta.addr); | 972 | sta->sta.addr); |
973 | ieee80211_mps_sta_status_update(sta); | ||
974 | ieee80211_mps_set_sta_local_pm(sta, | ||
975 | mshcfg->power_mode); | ||
962 | break; | 976 | break; |
963 | default: | 977 | default: |
964 | spin_unlock_bh(&sta->lock); | 978 | spin_unlock_bh(&sta->lock); |
@@ -998,6 +1012,9 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m | |||
998 | mesh_plink_frame_tx(sdata, | 1012 | mesh_plink_frame_tx(sdata, |
999 | WLAN_SP_MESH_PEERING_CONFIRM, | 1013 | WLAN_SP_MESH_PEERING_CONFIRM, |
1000 | sta->sta.addr, llid, plid, 0); | 1014 | sta->sta.addr, llid, plid, 0); |
1015 | ieee80211_mps_sta_status_update(sta); | ||
1016 | ieee80211_mps_set_sta_local_pm(sta, | ||
1017 | mshcfg->power_mode); | ||
1001 | break; | 1018 | break; |
1002 | default: | 1019 | default: |
1003 | spin_unlock_bh(&sta->lock); | 1020 | spin_unlock_bh(&sta->lock); |
diff --git a/net/mac80211/mesh_ps.c b/net/mac80211/mesh_ps.c new file mode 100644 index 000000000000..b677962525ed --- /dev/null +++ b/net/mac80211/mesh_ps.c | |||
@@ -0,0 +1,585 @@ | |||
1 | /* | ||
2 | * Copyright 2012-2013, Marco Porsch <marco.porsch@s2005.tu-chemnitz.de> | ||
3 | * Copyright 2012-2013, cozybit Inc. | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License version 2 as | ||
7 | * published by the Free Software Foundation. | ||
8 | */ | ||
9 | |||
10 | #include "mesh.h" | ||
11 | #include "wme.h" | ||
12 | |||
13 | |||
14 | /* mesh PS management */ | ||
15 | |||
16 | /** | ||
17 | * mps_qos_null_get - create pre-addressed QoS Null frame for mesh powersave | ||
18 | */ | ||
19 | static struct sk_buff *mps_qos_null_get(struct sta_info *sta) | ||
20 | { | ||
21 | struct ieee80211_sub_if_data *sdata = sta->sdata; | ||
22 | struct ieee80211_local *local = sdata->local; | ||
23 | struct ieee80211_hdr *nullfunc; /* use 4addr header */ | ||
24 | struct sk_buff *skb; | ||
25 | int size = sizeof(*nullfunc); | ||
26 | __le16 fc; | ||
27 | |||
28 | skb = dev_alloc_skb(local->hw.extra_tx_headroom + size + 2); | ||
29 | if (!skb) | ||
30 | return NULL; | ||
31 | skb_reserve(skb, local->hw.extra_tx_headroom); | ||
32 | |||
33 | nullfunc = (struct ieee80211_hdr *) skb_put(skb, size); | ||
34 | fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_QOS_NULLFUNC); | ||
35 | ieee80211_fill_mesh_addresses(nullfunc, &fc, sta->sta.addr, | ||
36 | sdata->vif.addr); | ||
37 | nullfunc->frame_control = fc; | ||
38 | nullfunc->duration_id = 0; | ||
39 | /* no address resolution for this frame -> set addr 1 immediately */ | ||
40 | memcpy(nullfunc->addr1, sta->sta.addr, ETH_ALEN); | ||
41 | memset(skb_put(skb, 2), 0, 2); /* append QoS control field */ | ||
42 | ieee80211_mps_set_frame_flags(sdata, sta, nullfunc); | ||
43 | |||
44 | return skb; | ||
45 | } | ||
46 | |||
47 | /** | ||
48 | * mps_qos_null_tx - send a QoS Null to indicate link-specific power mode | ||
49 | */ | ||
50 | static void mps_qos_null_tx(struct sta_info *sta) | ||
51 | { | ||
52 | struct sk_buff *skb; | ||
53 | |||
54 | skb = mps_qos_null_get(sta); | ||
55 | if (!skb) | ||
56 | return; | ||
57 | |||
58 | mps_dbg(sta->sdata, "announcing peer-specific power mode to %pM\n", | ||
59 | sta->sta.addr); | ||
60 | |||
61 | /* don't unintentionally start a MPSP */ | ||
62 | if (!test_sta_flag(sta, WLAN_STA_PS_STA)) { | ||
63 | u8 *qc = ieee80211_get_qos_ctl((void *) skb->data); | ||
64 | |||
65 | qc[0] |= IEEE80211_QOS_CTL_EOSP; | ||
66 | } | ||
67 | |||
68 | ieee80211_tx_skb(sta->sdata, skb); | ||
69 | } | ||
70 | |||
71 | /** | ||
72 | * ieee80211_mps_local_status_update - track status of local link-specific PMs | ||
73 | * | ||
74 | * @sdata: local mesh subif | ||
75 | * | ||
76 | * sets the non-peer power mode and triggers the driver PS (re-)configuration | ||
77 | */ | ||
78 | void ieee80211_mps_local_status_update(struct ieee80211_sub_if_data *sdata) | ||
79 | { | ||
80 | struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; | ||
81 | struct sta_info *sta; | ||
82 | bool peering = false; | ||
83 | int light_sleep_cnt = 0; | ||
84 | int deep_sleep_cnt = 0; | ||
85 | |||
86 | rcu_read_lock(); | ||
87 | list_for_each_entry_rcu(sta, &sdata->local->sta_list, list) { | ||
88 | if (sdata != sta->sdata) | ||
89 | continue; | ||
90 | |||
91 | switch (sta->plink_state) { | ||
92 | case NL80211_PLINK_OPN_SNT: | ||
93 | case NL80211_PLINK_OPN_RCVD: | ||
94 | case NL80211_PLINK_CNF_RCVD: | ||
95 | peering = true; | ||
96 | break; | ||
97 | case NL80211_PLINK_ESTAB: | ||
98 | if (sta->local_pm == NL80211_MESH_POWER_LIGHT_SLEEP) | ||
99 | light_sleep_cnt++; | ||
100 | else if (sta->local_pm == NL80211_MESH_POWER_DEEP_SLEEP) | ||
101 | deep_sleep_cnt++; | ||
102 | break; | ||
103 | default: | ||
104 | break; | ||
105 | } | ||
106 | } | ||
107 | rcu_read_unlock(); | ||
108 | |||
109 | /* | ||
110 | * Set non-peer mode to active during peering/scanning/authentication | ||
111 | * (see IEEE802.11-2012 13.14.8.3). The non-peer mesh power mode is | ||
112 | * deep sleep if the local STA is in light or deep sleep towards at | ||
113 | * least one mesh peer (see 13.14.3.1). Otherwise, set it to the | ||
114 | * user-configured default value. | ||
115 | */ | ||
116 | if (peering) { | ||
117 | mps_dbg(sdata, "setting non-peer PM to active for peering\n"); | ||
118 | ifmsh->nonpeer_pm = NL80211_MESH_POWER_ACTIVE; | ||
119 | } else if (light_sleep_cnt || deep_sleep_cnt) { | ||
120 | mps_dbg(sdata, "setting non-peer PM to deep sleep\n"); | ||
121 | ifmsh->nonpeer_pm = NL80211_MESH_POWER_DEEP_SLEEP; | ||
122 | } else { | ||
123 | mps_dbg(sdata, "setting non-peer PM to user value\n"); | ||
124 | ifmsh->nonpeer_pm = ifmsh->mshcfg.power_mode; | ||
125 | } | ||
126 | |||
127 | ifmsh->ps_peers_light_sleep = light_sleep_cnt; | ||
128 | ifmsh->ps_peers_deep_sleep = deep_sleep_cnt; | ||
129 | } | ||
130 | |||
131 | /** | ||
132 | * ieee80211_mps_set_sta_local_pm - set local PM towards a mesh STA | ||
133 | * | ||
134 | * @sta: mesh STA | ||
135 | * @pm: the power mode to set | ||
136 | */ | ||
137 | void ieee80211_mps_set_sta_local_pm(struct sta_info *sta, | ||
138 | enum nl80211_mesh_power_mode pm) | ||
139 | { | ||
140 | struct ieee80211_sub_if_data *sdata = sta->sdata; | ||
141 | |||
142 | mps_dbg(sdata, "local STA operates in mode %d with %pM\n", | ||
143 | pm, sta->sta.addr); | ||
144 | |||
145 | sta->local_pm = pm; | ||
146 | |||
147 | /* | ||
148 | * announce peer-specific power mode transition | ||
149 | * (see IEEE802.11-2012 13.14.3.2 and 13.14.3.3) | ||
150 | */ | ||
151 | if (sta->plink_state == NL80211_PLINK_ESTAB) | ||
152 | mps_qos_null_tx(sta); | ||
153 | |||
154 | ieee80211_mps_local_status_update(sdata); | ||
155 | } | ||
156 | |||
157 | /** | ||
158 | * ieee80211_mps_set_frame_flags - set mesh PS flags in FC (and QoS Control) | ||
159 | * | ||
160 | * @sdata: local mesh subif | ||
161 | * @sta: mesh STA | ||
162 | * @hdr: 802.11 frame header | ||
163 | * | ||
164 | * see IEEE802.11-2012 8.2.4.1.7 and 8.2.4.5.11 | ||
165 | * | ||
166 | * NOTE: sta must be given when an individually-addressed QoS frame header | ||
167 | * is handled, for group-addressed and management frames it is not used | ||
168 | */ | ||
169 | void ieee80211_mps_set_frame_flags(struct ieee80211_sub_if_data *sdata, | ||
170 | struct sta_info *sta, | ||
171 | struct ieee80211_hdr *hdr) | ||
172 | { | ||
173 | enum nl80211_mesh_power_mode pm; | ||
174 | u8 *qc; | ||
175 | |||
176 | if (WARN_ON(is_unicast_ether_addr(hdr->addr1) && | ||
177 | ieee80211_is_data_qos(hdr->frame_control) && | ||
178 | !sta)) | ||
179 | return; | ||
180 | |||
181 | if (is_unicast_ether_addr(hdr->addr1) && | ||
182 | ieee80211_is_data_qos(hdr->frame_control) && | ||
183 | sta->plink_state == NL80211_PLINK_ESTAB) | ||
184 | pm = sta->local_pm; | ||
185 | else | ||
186 | pm = sdata->u.mesh.nonpeer_pm; | ||
187 | |||
188 | if (pm == NL80211_MESH_POWER_ACTIVE) | ||
189 | hdr->frame_control &= cpu_to_le16(~IEEE80211_FCTL_PM); | ||
190 | else | ||
191 | hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM); | ||
192 | |||
193 | if (!ieee80211_is_data_qos(hdr->frame_control)) | ||
194 | return; | ||
195 | |||
196 | qc = ieee80211_get_qos_ctl(hdr); | ||
197 | |||
198 | if ((is_unicast_ether_addr(hdr->addr1) && | ||
199 | pm == NL80211_MESH_POWER_DEEP_SLEEP) || | ||
200 | (is_multicast_ether_addr(hdr->addr1) && | ||
201 | sdata->u.mesh.ps_peers_deep_sleep > 0)) | ||
202 | qc[1] |= (IEEE80211_QOS_CTL_MESH_PS_LEVEL >> 8); | ||
203 | else | ||
204 | qc[1] &= ~(IEEE80211_QOS_CTL_MESH_PS_LEVEL >> 8); | ||
205 | } | ||
206 | |||
207 | /** | ||
208 | * ieee80211_mps_sta_status_update - update buffering status of neighbor STA | ||
209 | * | ||
210 | * @sta: mesh STA | ||
211 | * | ||
212 | * called after change of peering status or non-peer/peer-specific power mode | ||
213 | */ | ||
214 | void ieee80211_mps_sta_status_update(struct sta_info *sta) | ||
215 | { | ||
216 | enum nl80211_mesh_power_mode pm; | ||
217 | bool do_buffer; | ||
218 | |||
219 | /* | ||
220 | * use peer-specific power mode if peering is established and the | ||
221 | * peer's power mode is known | ||
222 | */ | ||
223 | if (sta->plink_state == NL80211_PLINK_ESTAB && | ||
224 | sta->peer_pm != NL80211_MESH_POWER_UNKNOWN) | ||
225 | pm = sta->peer_pm; | ||
226 | else | ||
227 | pm = sta->nonpeer_pm; | ||
228 | |||
229 | do_buffer = (pm != NL80211_MESH_POWER_ACTIVE); | ||
230 | |||
231 | /* Don't let the same PS state be set twice */ | ||
232 | if (test_sta_flag(sta, WLAN_STA_PS_STA) == do_buffer) | ||
233 | return; | ||
234 | |||
235 | if (do_buffer) { | ||
236 | set_sta_flag(sta, WLAN_STA_PS_STA); | ||
237 | atomic_inc(&sta->sdata->u.mesh.ps.num_sta_ps); | ||
238 | mps_dbg(sta->sdata, "start PS buffering frames towards %pM\n", | ||
239 | sta->sta.addr); | ||
240 | } else { | ||
241 | ieee80211_sta_ps_deliver_wakeup(sta); | ||
242 | } | ||
243 | |||
244 | /* clear the MPSP flags for non-peers or active STA */ | ||
245 | if (sta->plink_state != NL80211_PLINK_ESTAB) { | ||
246 | clear_sta_flag(sta, WLAN_STA_MPSP_OWNER); | ||
247 | clear_sta_flag(sta, WLAN_STA_MPSP_RECIPIENT); | ||
248 | } else if (!do_buffer) { | ||
249 | clear_sta_flag(sta, WLAN_STA_MPSP_OWNER); | ||
250 | } | ||
251 | } | ||
252 | |||
253 | static void mps_set_sta_peer_pm(struct sta_info *sta, | ||
254 | struct ieee80211_hdr *hdr) | ||
255 | { | ||
256 | enum nl80211_mesh_power_mode pm; | ||
257 | u8 *qc = ieee80211_get_qos_ctl(hdr); | ||
258 | |||
259 | /* | ||
260 | * Test Power Management field of frame control (PW) and | ||
261 | * mesh power save level subfield of QoS control field (PSL) | ||
262 | * | ||
263 | * | PM | PSL| Mesh PM | | ||
264 | * +----+----+---------+ | ||
265 | * | 0 |Rsrv| Active | | ||
266 | * | 1 | 0 | Light | | ||
267 | * | 1 | 1 | Deep | | ||
268 | */ | ||
269 | if (ieee80211_has_pm(hdr->frame_control)) { | ||
270 | if (qc[1] & (IEEE80211_QOS_CTL_MESH_PS_LEVEL >> 8)) | ||
271 | pm = NL80211_MESH_POWER_DEEP_SLEEP; | ||
272 | else | ||
273 | pm = NL80211_MESH_POWER_LIGHT_SLEEP; | ||
274 | } else { | ||
275 | pm = NL80211_MESH_POWER_ACTIVE; | ||
276 | } | ||
277 | |||
278 | if (sta->peer_pm == pm) | ||
279 | return; | ||
280 | |||
281 | mps_dbg(sta->sdata, "STA %pM enters mode %d\n", | ||
282 | sta->sta.addr, pm); | ||
283 | |||
284 | sta->peer_pm = pm; | ||
285 | |||
286 | ieee80211_mps_sta_status_update(sta); | ||
287 | } | ||
288 | |||
289 | static void mps_set_sta_nonpeer_pm(struct sta_info *sta, | ||
290 | struct ieee80211_hdr *hdr) | ||
291 | { | ||
292 | enum nl80211_mesh_power_mode pm; | ||
293 | |||
294 | if (ieee80211_has_pm(hdr->frame_control)) | ||
295 | pm = NL80211_MESH_POWER_DEEP_SLEEP; | ||
296 | else | ||
297 | pm = NL80211_MESH_POWER_ACTIVE; | ||
298 | |||
299 | if (sta->nonpeer_pm == pm) | ||
300 | return; | ||
301 | |||
302 | mps_dbg(sta->sdata, "STA %pM sets non-peer mode to %d\n", | ||
303 | sta->sta.addr, pm); | ||
304 | |||
305 | sta->nonpeer_pm = pm; | ||
306 | |||
307 | ieee80211_mps_sta_status_update(sta); | ||
308 | } | ||
309 | |||
310 | /** | ||
311 | * ieee80211_mps_rx_h_sta_process - frame receive handler for mesh powersave | ||
312 | * | ||
313 | * @sta: STA info that transmitted the frame | ||
314 | * @hdr: IEEE 802.11 (QoS) Header | ||
315 | */ | ||
316 | void ieee80211_mps_rx_h_sta_process(struct sta_info *sta, | ||
317 | struct ieee80211_hdr *hdr) | ||
318 | { | ||
319 | if (is_unicast_ether_addr(hdr->addr1) && | ||
320 | ieee80211_is_data_qos(hdr->frame_control)) { | ||
321 | /* | ||
322 | * individually addressed QoS Data/Null frames contain | ||
323 | * peer link-specific PS mode towards the local STA | ||
324 | */ | ||
325 | mps_set_sta_peer_pm(sta, hdr); | ||
326 | |||
327 | /* check for mesh Peer Service Period trigger frames */ | ||
328 | ieee80211_mpsp_trigger_process(ieee80211_get_qos_ctl(hdr), | ||
329 | sta, false, false); | ||
330 | } else { | ||
331 | /* | ||
332 | * can only determine non-peer PS mode | ||
333 | * (see IEEE802.11-2012 8.2.4.1.7) | ||
334 | */ | ||
335 | mps_set_sta_nonpeer_pm(sta, hdr); | ||
336 | } | ||
337 | } | ||
338 | |||
339 | |||
340 | /* mesh PS frame release */ | ||
341 | |||
342 | static void mpsp_trigger_send(struct sta_info *sta, bool rspi, bool eosp) | ||
343 | { | ||
344 | struct ieee80211_sub_if_data *sdata = sta->sdata; | ||
345 | struct sk_buff *skb; | ||
346 | struct ieee80211_hdr *nullfunc; | ||
347 | struct ieee80211_tx_info *info; | ||
348 | u8 *qc; | ||
349 | |||
350 | skb = mps_qos_null_get(sta); | ||
351 | if (!skb) | ||
352 | return; | ||
353 | |||
354 | nullfunc = (struct ieee80211_hdr *) skb->data; | ||
355 | if (!eosp) | ||
356 | nullfunc->frame_control |= | ||
357 | cpu_to_le16(IEEE80211_FCTL_MOREDATA); | ||
358 | /* | ||
359 | * | RSPI | EOSP | MPSP triggering | | ||
360 | * +------+------+--------------------+ | ||
361 | * | 0 | 0 | local STA is owner | | ||
362 | * | 0 | 1 | no MPSP (MPSP end) | | ||
363 | * | 1 | 0 | both STA are owner | | ||
364 | * | 1 | 1 | peer STA is owner | see IEEE802.11-2012 13.14.9.2 | ||
365 | */ | ||
366 | qc = ieee80211_get_qos_ctl(nullfunc); | ||
367 | if (rspi) | ||
368 | qc[1] |= (IEEE80211_QOS_CTL_RSPI >> 8); | ||
369 | if (eosp) | ||
370 | qc[0] |= IEEE80211_QOS_CTL_EOSP; | ||
371 | |||
372 | info = IEEE80211_SKB_CB(skb); | ||
373 | |||
374 | info->flags |= IEEE80211_TX_CTL_NO_PS_BUFFER | | ||
375 | IEEE80211_TX_CTL_REQ_TX_STATUS; | ||
376 | |||
377 | mps_dbg(sdata, "sending MPSP trigger%s%s to %pM\n", | ||
378 | rspi ? " RSPI" : "", eosp ? " EOSP" : "", sta->sta.addr); | ||
379 | |||
380 | ieee80211_tx_skb(sdata, skb); | ||
381 | } | ||
382 | |||
383 | /** | ||
384 | * mpsp_qos_null_append - append QoS Null frame to MPSP skb queue if needed | ||
385 | * | ||
386 | * To properly end a mesh MPSP the last transmitted frame has to set the EOSP | ||
387 | * flag in the QoS Control field. In case the current tailing frame is not a | ||
388 | * QoS Data frame, append a QoS Null to carry the flag. | ||
389 | */ | ||
390 | static void mpsp_qos_null_append(struct sta_info *sta, | ||
391 | struct sk_buff_head *frames) | ||
392 | { | ||
393 | struct ieee80211_sub_if_data *sdata = sta->sdata; | ||
394 | struct sk_buff *new_skb, *skb = skb_peek_tail(frames); | ||
395 | struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; | ||
396 | struct ieee80211_tx_info *info; | ||
397 | |||
398 | if (ieee80211_is_data_qos(hdr->frame_control)) | ||
399 | return; | ||
400 | |||
401 | new_skb = mps_qos_null_get(sta); | ||
402 | if (!new_skb) | ||
403 | return; | ||
404 | |||
405 | mps_dbg(sdata, "appending QoS Null in MPSP towards %pM\n", | ||
406 | sta->sta.addr); | ||
407 | /* | ||
408 | * This frame has to be transmitted last. Assign lowest priority to | ||
409 | * make sure it cannot pass other frames when releasing multiple ACs. | ||
410 | */ | ||
411 | new_skb->priority = 1; | ||
412 | skb_set_queue_mapping(new_skb, IEEE80211_AC_BK); | ||
413 | ieee80211_set_qos_hdr(sdata, new_skb); | ||
414 | |||
415 | info = IEEE80211_SKB_CB(new_skb); | ||
416 | info->control.vif = &sdata->vif; | ||
417 | info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING; | ||
418 | |||
419 | __skb_queue_tail(frames, new_skb); | ||
420 | } | ||
421 | |||
422 | /** | ||
423 | * mps_frame_deliver - transmit frames during mesh powersave | ||
424 | * | ||
425 | * @sta: STA info to transmit to | ||
426 | * @n_frames: number of frames to transmit. -1 for all | ||
427 | */ | ||
428 | static void mps_frame_deliver(struct sta_info *sta, int n_frames) | ||
429 | { | ||
430 | struct ieee80211_sub_if_data *sdata = sta->sdata; | ||
431 | struct ieee80211_local *local = sdata->local; | ||
432 | int ac; | ||
433 | struct sk_buff_head frames; | ||
434 | struct sk_buff *skb; | ||
435 | bool more_data = false; | ||
436 | |||
437 | skb_queue_head_init(&frames); | ||
438 | |||
439 | /* collect frame(s) from buffers */ | ||
440 | for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { | ||
441 | while (n_frames != 0) { | ||
442 | skb = skb_dequeue(&sta->tx_filtered[ac]); | ||
443 | if (!skb) { | ||
444 | skb = skb_dequeue( | ||
445 | &sta->ps_tx_buf[ac]); | ||
446 | if (skb) | ||
447 | local->total_ps_buffered--; | ||
448 | } | ||
449 | if (!skb) | ||
450 | break; | ||
451 | n_frames--; | ||
452 | __skb_queue_tail(&frames, skb); | ||
453 | } | ||
454 | |||
455 | if (!skb_queue_empty(&sta->tx_filtered[ac]) || | ||
456 | !skb_queue_empty(&sta->ps_tx_buf[ac])) | ||
457 | more_data = true; | ||
458 | } | ||
459 | |||
460 | /* nothing to send? -> EOSP */ | ||
461 | if (skb_queue_empty(&frames)) { | ||
462 | mpsp_trigger_send(sta, false, true); | ||
463 | return; | ||
464 | } | ||
465 | |||
466 | /* in a MPSP make sure the last skb is a QoS Data frame */ | ||
467 | if (test_sta_flag(sta, WLAN_STA_MPSP_OWNER)) | ||
468 | mpsp_qos_null_append(sta, &frames); | ||
469 | |||
470 | mps_dbg(sta->sdata, "sending %d frames to PS STA %pM\n", | ||
471 | skb_queue_len(&frames), sta->sta.addr); | ||
472 | |||
473 | /* prepare collected frames for transmission */ | ||
474 | skb_queue_walk(&frames, skb) { | ||
475 | struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); | ||
476 | struct ieee80211_hdr *hdr = (void *) skb->data; | ||
477 | |||
478 | /* | ||
479 | * Tell TX path to send this frame even though the | ||
480 | * STA may still remain is PS mode after this frame | ||
481 | * exchange. | ||
482 | */ | ||
483 | info->flags |= IEEE80211_TX_CTL_NO_PS_BUFFER; | ||
484 | |||
485 | if (more_data || !skb_queue_is_last(&frames, skb)) | ||
486 | hdr->frame_control |= | ||
487 | cpu_to_le16(IEEE80211_FCTL_MOREDATA); | ||
488 | else | ||
489 | hdr->frame_control &= | ||
490 | cpu_to_le16(~IEEE80211_FCTL_MOREDATA); | ||
491 | |||
492 | if (skb_queue_is_last(&frames, skb) && | ||
493 | ieee80211_is_data_qos(hdr->frame_control)) { | ||
494 | u8 *qoshdr = ieee80211_get_qos_ctl(hdr); | ||
495 | |||
496 | /* MPSP trigger frame ends service period */ | ||
497 | *qoshdr |= IEEE80211_QOS_CTL_EOSP; | ||
498 | info->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS; | ||
499 | } | ||
500 | } | ||
501 | |||
502 | ieee80211_add_pending_skbs(local, &frames); | ||
503 | sta_info_recalc_tim(sta); | ||
504 | } | ||
505 | |||
506 | /** | ||
507 | * ieee80211_mpsp_trigger_process - track status of mesh Peer Service Periods | ||
508 | * | ||
509 | * @qc: QoS Control field | ||
510 | * @sta: peer to start a MPSP with | ||
511 | * @tx: frame was transmitted by the local STA | ||
512 | * @acked: frame has been transmitted successfully | ||
513 | * | ||
514 | * NOTE: active mode STA may only serve as MPSP owner | ||
515 | */ | ||
516 | void ieee80211_mpsp_trigger_process(u8 *qc, struct sta_info *sta, | ||
517 | bool tx, bool acked) | ||
518 | { | ||
519 | u8 rspi = qc[1] & (IEEE80211_QOS_CTL_RSPI >> 8); | ||
520 | u8 eosp = qc[0] & IEEE80211_QOS_CTL_EOSP; | ||
521 | |||
522 | if (tx) { | ||
523 | if (rspi && acked) | ||
524 | set_sta_flag(sta, WLAN_STA_MPSP_RECIPIENT); | ||
525 | |||
526 | if (eosp) | ||
527 | clear_sta_flag(sta, WLAN_STA_MPSP_OWNER); | ||
528 | else if (acked && | ||
529 | test_sta_flag(sta, WLAN_STA_PS_STA) && | ||
530 | !test_and_set_sta_flag(sta, WLAN_STA_MPSP_OWNER)) | ||
531 | mps_frame_deliver(sta, -1); | ||
532 | } else { | ||
533 | if (eosp) | ||
534 | clear_sta_flag(sta, WLAN_STA_MPSP_RECIPIENT); | ||
535 | else if (sta->local_pm != NL80211_MESH_POWER_ACTIVE) | ||
536 | set_sta_flag(sta, WLAN_STA_MPSP_RECIPIENT); | ||
537 | |||
538 | if (rspi && !test_and_set_sta_flag(sta, WLAN_STA_MPSP_OWNER)) | ||
539 | mps_frame_deliver(sta, -1); | ||
540 | } | ||
541 | } | ||
542 | |||
543 | /** | ||
544 | * ieee80211_mps_frame_release - release buffered frames in response to beacon | ||
545 | * | ||
546 | * @sta: mesh STA | ||
547 | * @elems: beacon IEs | ||
548 | * | ||
549 | * For peers if we have individually-addressed frames buffered or the peer | ||
550 | * indicates buffered frames, send a corresponding MPSP trigger frame. Since | ||
551 | * we do not evaluate the awake window duration, QoS Nulls are used as MPSP | ||
552 | * trigger frames. If the neighbour STA is not a peer, only send single frames. | ||
553 | */ | ||
554 | void ieee80211_mps_frame_release(struct sta_info *sta, | ||
555 | struct ieee802_11_elems *elems) | ||
556 | { | ||
557 | int ac, buffer_local = 0; | ||
558 | bool has_buffered = false; | ||
559 | |||
560 | /* TIM map only for LLID <= IEEE80211_MAX_AID */ | ||
561 | if (sta->plink_state == NL80211_PLINK_ESTAB) | ||
562 | has_buffered = ieee80211_check_tim(elems->tim, elems->tim_len, | ||
563 | le16_to_cpu(sta->llid) % IEEE80211_MAX_AID); | ||
564 | |||
565 | if (has_buffered) | ||
566 | mps_dbg(sta->sdata, "%pM indicates buffered frames\n", | ||
567 | sta->sta.addr); | ||
568 | |||
569 | /* only transmit to PS STA with announced, non-zero awake window */ | ||
570 | if (test_sta_flag(sta, WLAN_STA_PS_STA) && | ||
571 | (!elems->awake_window || !le16_to_cpu(*elems->awake_window))) | ||
572 | return; | ||
573 | |||
574 | for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) | ||
575 | buffer_local += skb_queue_len(&sta->ps_tx_buf[ac]) + | ||
576 | skb_queue_len(&sta->tx_filtered[ac]); | ||
577 | |||
578 | if (!has_buffered && !buffer_local) | ||
579 | return; | ||
580 | |||
581 | if (sta->plink_state == NL80211_PLINK_ESTAB) | ||
582 | mpsp_trigger_send(sta, has_buffered, !buffer_local); | ||
583 | else | ||
584 | mps_frame_deliver(sta, 1); | ||
585 | } | ||
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index a19089565c4b..c98be0593756 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c | |||
@@ -1452,6 +1452,10 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx) | |||
1452 | } | 1452 | } |
1453 | } | 1453 | } |
1454 | 1454 | ||
1455 | /* mesh power save support */ | ||
1456 | if (ieee80211_vif_is_mesh(&rx->sdata->vif)) | ||
1457 | ieee80211_mps_rx_h_sta_process(sta, hdr); | ||
1458 | |||
1455 | /* | 1459 | /* |
1456 | * Drop (qos-)data::nullfunc frames silently, since they | 1460 | * Drop (qos-)data::nullfunc frames silently, since they |
1457 | * are used only to control station power saving mode. | 1461 | * are used only to control station power saving mode. |
@@ -2090,7 +2094,10 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx) | |||
2090 | if (is_multicast_ether_addr(fwd_hdr->addr1)) { | 2094 | if (is_multicast_ether_addr(fwd_hdr->addr1)) { |
2091 | IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, fwded_mcast); | 2095 | IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, fwded_mcast); |
2092 | memcpy(fwd_hdr->addr2, sdata->vif.addr, ETH_ALEN); | 2096 | memcpy(fwd_hdr->addr2, sdata->vif.addr, ETH_ALEN); |
2097 | /* update power mode indication when forwarding */ | ||
2098 | ieee80211_mps_set_frame_flags(sdata, NULL, fwd_hdr); | ||
2093 | } else if (!mesh_nexthop_lookup(fwd_skb, sdata)) { | 2099 | } else if (!mesh_nexthop_lookup(fwd_skb, sdata)) { |
2100 | /* mesh power mode flags updated in mesh_nexthop_lookup */ | ||
2094 | IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, fwded_unicast); | 2101 | IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, fwded_unicast); |
2095 | } else { | 2102 | } else { |
2096 | /* unable to resolve next hop */ | 2103 | /* unable to resolve next hop */ |
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 227233c3ff7f..47a0f0601768 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c | |||
@@ -120,6 +120,8 @@ static void cleanup_single_sta(struct sta_info *sta) | |||
120 | if (sta->sdata->vif.type == NL80211_IFTYPE_AP || | 120 | if (sta->sdata->vif.type == NL80211_IFTYPE_AP || |
121 | sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN) | 121 | sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN) |
122 | ps = &sdata->bss->ps; | 122 | ps = &sdata->bss->ps; |
123 | else if (ieee80211_vif_is_mesh(&sdata->vif)) | ||
124 | ps = &sdata->u.mesh.ps; | ||
123 | else | 125 | else |
124 | return; | 126 | return; |
125 | 127 | ||
@@ -587,6 +589,12 @@ void sta_info_recalc_tim(struct sta_info *sta) | |||
587 | 589 | ||
588 | ps = &sta->sdata->bss->ps; | 590 | ps = &sta->sdata->bss->ps; |
589 | id = sta->sta.aid; | 591 | id = sta->sta.aid; |
592 | #ifdef CONFIG_MAC80211_MESH | ||
593 | } else if (ieee80211_vif_is_mesh(&sta->sdata->vif)) { | ||
594 | ps = &sta->sdata->u.mesh.ps; | ||
595 | /* TIM map only for PLID <= IEEE80211_MAX_AID */ | ||
596 | id = le16_to_cpu(sta->plid) % IEEE80211_MAX_AID; | ||
597 | #endif | ||
590 | } else { | 598 | } else { |
591 | return; | 599 | return; |
592 | } | 600 | } |
@@ -745,8 +753,9 @@ static bool sta_info_cleanup_expire_buffered(struct ieee80211_local *local, | |||
745 | bool have_buffered = false; | 753 | bool have_buffered = false; |
746 | int ac; | 754 | int ac; |
747 | 755 | ||
748 | /* This is only necessary for stations on BSS interfaces */ | 756 | /* This is only necessary for stations on BSS/MBSS interfaces */ |
749 | if (!sta->sdata->bss) | 757 | if (!sta->sdata->bss && |
758 | !ieee80211_vif_is_mesh(&sta->sdata->vif)) | ||
750 | return false; | 759 | return false; |
751 | 760 | ||
752 | for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) | 761 | for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) |
@@ -934,6 +943,11 @@ void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata, | |||
934 | if (time_after(jiffies, sta->last_rx + exp_time)) { | 943 | if (time_after(jiffies, sta->last_rx + exp_time)) { |
935 | sta_dbg(sta->sdata, "expiring inactive STA %pM\n", | 944 | sta_dbg(sta->sdata, "expiring inactive STA %pM\n", |
936 | sta->sta.addr); | 945 | sta->sta.addr); |
946 | |||
947 | if (ieee80211_vif_is_mesh(&sdata->vif) && | ||
948 | test_sta_flag(sta, WLAN_STA_PS_STA)) | ||
949 | atomic_dec(&sdata->u.mesh.ps.num_sta_ps); | ||
950 | |||
937 | WARN_ON(__sta_info_destroy(sta)); | 951 | WARN_ON(__sta_info_destroy(sta)); |
938 | } | 952 | } |
939 | } | 953 | } |
@@ -992,6 +1006,8 @@ static void clear_sta_ps_flags(void *_sta) | |||
992 | if (sdata->vif.type == NL80211_IFTYPE_AP || | 1006 | if (sdata->vif.type == NL80211_IFTYPE_AP || |
993 | sdata->vif.type == NL80211_IFTYPE_AP_VLAN) | 1007 | sdata->vif.type == NL80211_IFTYPE_AP_VLAN) |
994 | ps = &sdata->bss->ps; | 1008 | ps = &sdata->bss->ps; |
1009 | else if (ieee80211_vif_is_mesh(&sdata->vif)) | ||
1010 | ps = &sdata->u.mesh.ps; | ||
995 | else | 1011 | else |
996 | return; | 1012 | return; |
997 | 1013 | ||
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index af7d78aa5523..5a1deba2c645 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h | |||
@@ -56,6 +56,8 @@ | |||
56 | * @WLAN_STA_INSERTED: This station is inserted into the hash table. | 56 | * @WLAN_STA_INSERTED: This station is inserted into the hash table. |
57 | * @WLAN_STA_RATE_CONTROL: rate control was initialized for this station. | 57 | * @WLAN_STA_RATE_CONTROL: rate control was initialized for this station. |
58 | * @WLAN_STA_TOFFSET_KNOWN: toffset calculated for this station is valid. | 58 | * @WLAN_STA_TOFFSET_KNOWN: toffset calculated for this station is valid. |
59 | * @WLAN_STA_MPSP_OWNER: local STA is owner of a mesh Peer Service Period. | ||
60 | * @WLAN_STA_MPSP_RECIPIENT: local STA is recipient of a MPSP. | ||
59 | */ | 61 | */ |
60 | enum ieee80211_sta_info_flags { | 62 | enum ieee80211_sta_info_flags { |
61 | WLAN_STA_AUTH, | 63 | WLAN_STA_AUTH, |
@@ -78,6 +80,8 @@ enum ieee80211_sta_info_flags { | |||
78 | WLAN_STA_INSERTED, | 80 | WLAN_STA_INSERTED, |
79 | WLAN_STA_RATE_CONTROL, | 81 | WLAN_STA_RATE_CONTROL, |
80 | WLAN_STA_TOFFSET_KNOWN, | 82 | WLAN_STA_TOFFSET_KNOWN, |
83 | WLAN_STA_MPSP_OWNER, | ||
84 | WLAN_STA_MPSP_RECIPIENT, | ||
81 | }; | 85 | }; |
82 | 86 | ||
83 | #define ADDBA_RESP_INTERVAL HZ | 87 | #define ADDBA_RESP_INTERVAL HZ |
@@ -282,6 +286,9 @@ struct sta_ampdu_mlme { | |||
282 | * @t_offset_setpoint: reference timing offset of this sta to be used when | 286 | * @t_offset_setpoint: reference timing offset of this sta to be used when |
283 | * calculating clockdrift | 287 | * calculating clockdrift |
284 | * @ch_width: peer's channel width | 288 | * @ch_width: peer's channel width |
289 | * @local_pm: local link-specific power save mode | ||
290 | * @peer_pm: peer-specific power save mode towards local STA | ||
291 | * @nonpeer_pm: STA power save mode towards non-peer neighbors | ||
285 | * @debugfs: debug filesystem info | 292 | * @debugfs: debug filesystem info |
286 | * @dead: set to true when sta is unlinked | 293 | * @dead: set to true when sta is unlinked |
287 | * @uploaded: set to true when sta is uploaded to the driver | 294 | * @uploaded: set to true when sta is uploaded to the driver |
@@ -379,6 +386,10 @@ struct sta_info { | |||
379 | s64 t_offset; | 386 | s64 t_offset; |
380 | s64 t_offset_setpoint; | 387 | s64 t_offset_setpoint; |
381 | enum nl80211_chan_width ch_width; | 388 | enum nl80211_chan_width ch_width; |
389 | /* mesh power save */ | ||
390 | enum nl80211_mesh_power_mode local_pm; | ||
391 | enum nl80211_mesh_power_mode peer_pm; | ||
392 | enum nl80211_mesh_power_mode nonpeer_pm; | ||
382 | #endif | 393 | #endif |
383 | 394 | ||
384 | #ifdef CONFIG_MAC80211_DEBUGFS | 395 | #ifdef CONFIG_MAC80211_DEBUGFS |
diff --git a/net/mac80211/status.c b/net/mac80211/status.c index d041de056b7f..43439203f4e4 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c | |||
@@ -472,6 +472,13 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) | |||
472 | return; | 472 | return; |
473 | } | 473 | } |
474 | 474 | ||
475 | /* mesh Peer Service Period support */ | ||
476 | if (ieee80211_vif_is_mesh(&sta->sdata->vif) && | ||
477 | ieee80211_is_data_qos(fc)) | ||
478 | ieee80211_mpsp_trigger_process( | ||
479 | ieee80211_get_qos_ctl(hdr), | ||
480 | sta, true, acked); | ||
481 | |||
475 | if ((local->hw.flags & IEEE80211_HW_HAS_RATE_CONTROL) && | 482 | if ((local->hw.flags & IEEE80211_HW_HAS_RATE_CONTROL) && |
476 | (rates_idx != -1)) | 483 | (rates_idx != -1)) |
477 | sta->last_tx_rate = info->status.rates[rates_idx]; | 484 | sta->last_tx_rate = info->status.rates[rates_idx]; |
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 7892b0a8873e..2ef0e19b06bb 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c | |||
@@ -329,6 +329,8 @@ static void purge_old_ps_buffers(struct ieee80211_local *local) | |||
329 | 329 | ||
330 | if (sdata->vif.type == NL80211_IFTYPE_AP) | 330 | if (sdata->vif.type == NL80211_IFTYPE_AP) |
331 | ps = &sdata->u.ap.ps; | 331 | ps = &sdata->u.ap.ps; |
332 | else if (ieee80211_vif_is_mesh(&sdata->vif)) | ||
333 | ps = &sdata->u.mesh.ps; | ||
332 | else | 334 | else |
333 | continue; | 335 | continue; |
334 | 336 | ||
@@ -372,18 +374,20 @@ ieee80211_tx_h_multicast_ps_buf(struct ieee80211_tx_data *tx) | |||
372 | /* | 374 | /* |
373 | * broadcast/multicast frame | 375 | * broadcast/multicast frame |
374 | * | 376 | * |
375 | * If any of the associated stations is in power save mode, | 377 | * If any of the associated/peer stations is in power save mode, |
376 | * the frame is buffered to be sent after DTIM beacon frame. | 378 | * the frame is buffered to be sent after DTIM beacon frame. |
377 | * This is done either by the hardware or us. | 379 | * This is done either by the hardware or us. |
378 | */ | 380 | */ |
379 | 381 | ||
380 | /* powersaving STAs currently only in AP/VLAN mode */ | 382 | /* powersaving STAs currently only in AP/VLAN/mesh mode */ |
381 | if (tx->sdata->vif.type == NL80211_IFTYPE_AP || | 383 | if (tx->sdata->vif.type == NL80211_IFTYPE_AP || |
382 | tx->sdata->vif.type == NL80211_IFTYPE_AP_VLAN) { | 384 | tx->sdata->vif.type == NL80211_IFTYPE_AP_VLAN) { |
383 | if (!tx->sdata->bss) | 385 | if (!tx->sdata->bss) |
384 | return TX_CONTINUE; | 386 | return TX_CONTINUE; |
385 | 387 | ||
386 | ps = &tx->sdata->bss->ps; | 388 | ps = &tx->sdata->bss->ps; |
389 | } else if (ieee80211_vif_is_mesh(&tx->sdata->vif)) { | ||
390 | ps = &tx->sdata->u.mesh.ps; | ||
387 | } else { | 391 | } else { |
388 | return TX_CONTINUE; | 392 | return TX_CONTINUE; |
389 | } | 393 | } |
@@ -1473,12 +1477,14 @@ void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, | |||
1473 | hdr = (struct ieee80211_hdr *) skb->data; | 1477 | hdr = (struct ieee80211_hdr *) skb->data; |
1474 | info->control.vif = &sdata->vif; | 1478 | info->control.vif = &sdata->vif; |
1475 | 1479 | ||
1476 | if (ieee80211_vif_is_mesh(&sdata->vif) && | 1480 | if (ieee80211_vif_is_mesh(&sdata->vif)) { |
1477 | ieee80211_is_data(hdr->frame_control) && | 1481 | if (ieee80211_is_data(hdr->frame_control) && |
1478 | !is_multicast_ether_addr(hdr->addr1) && | 1482 | is_unicast_ether_addr(hdr->addr1)) { |
1479 | mesh_nexthop_resolve(skb, sdata)) { | 1483 | if (mesh_nexthop_resolve(skb, sdata)) |
1480 | /* skb queued: don't free */ | 1484 | return; /* skb queued: don't free */ |
1481 | return; | 1485 | } else { |
1486 | ieee80211_mps_set_frame_flags(sdata, NULL, hdr); | ||
1487 | } | ||
1482 | } | 1488 | } |
1483 | 1489 | ||
1484 | ieee80211_set_qos_hdr(sdata, skb); | 1490 | ieee80211_set_qos_hdr(sdata, skb); |
@@ -2445,12 +2451,14 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, | |||
2445 | 2 + /* NULL SSID */ | 2451 | 2 + /* NULL SSID */ |
2446 | 2 + 8 + /* supported rates */ | 2452 | 2 + 8 + /* supported rates */ |
2447 | 2 + 3 + /* DS params */ | 2453 | 2 + 3 + /* DS params */ |
2454 | 256 + /* TIM IE */ | ||
2448 | 2 + (IEEE80211_MAX_SUPP_RATES - 8) + | 2455 | 2 + (IEEE80211_MAX_SUPP_RATES - 8) + |
2449 | 2 + sizeof(struct ieee80211_ht_cap) + | 2456 | 2 + sizeof(struct ieee80211_ht_cap) + |
2450 | 2 + sizeof(struct ieee80211_ht_operation) + | 2457 | 2 + sizeof(struct ieee80211_ht_operation) + |
2451 | 2 + sdata->u.mesh.mesh_id_len + | 2458 | 2 + sdata->u.mesh.mesh_id_len + |
2452 | 2 + sizeof(struct ieee80211_meshconf_ie) + | 2459 | 2 + sizeof(struct ieee80211_meshconf_ie) + |
2453 | sdata->u.mesh.ie_len); | 2460 | sdata->u.mesh.ie_len + |
2461 | 2 + sizeof(__le16)); /* awake window */ | ||
2454 | if (!skb) | 2462 | if (!skb) |
2455 | goto out; | 2463 | goto out; |
2456 | 2464 | ||
@@ -2462,6 +2470,7 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, | |||
2462 | eth_broadcast_addr(mgmt->da); | 2470 | eth_broadcast_addr(mgmt->da); |
2463 | memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); | 2471 | memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); |
2464 | memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN); | 2472 | memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN); |
2473 | ieee80211_mps_set_frame_flags(sdata, NULL, (void *) mgmt); | ||
2465 | mgmt->u.beacon.beacon_int = | 2474 | mgmt->u.beacon.beacon_int = |
2466 | cpu_to_le16(sdata->vif.bss_conf.beacon_int); | 2475 | cpu_to_le16(sdata->vif.bss_conf.beacon_int); |
2467 | mgmt->u.beacon.capab_info |= cpu_to_le16( | 2476 | mgmt->u.beacon.capab_info |= cpu_to_le16( |
@@ -2475,12 +2484,14 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, | |||
2475 | 2484 | ||
2476 | if (ieee80211_add_srates_ie(sdata, skb, true, band) || | 2485 | if (ieee80211_add_srates_ie(sdata, skb, true, band) || |
2477 | mesh_add_ds_params_ie(skb, sdata) || | 2486 | mesh_add_ds_params_ie(skb, sdata) || |
2487 | ieee80211_beacon_add_tim(sdata, &ifmsh->ps, skb) || | ||
2478 | ieee80211_add_ext_srates_ie(sdata, skb, true, band) || | 2488 | ieee80211_add_ext_srates_ie(sdata, skb, true, band) || |
2479 | mesh_add_rsn_ie(skb, sdata) || | 2489 | mesh_add_rsn_ie(skb, sdata) || |
2480 | mesh_add_ht_cap_ie(skb, sdata) || | 2490 | mesh_add_ht_cap_ie(skb, sdata) || |
2481 | mesh_add_ht_oper_ie(skb, sdata) || | 2491 | mesh_add_ht_oper_ie(skb, sdata) || |
2482 | mesh_add_meshid_ie(skb, sdata) || | 2492 | mesh_add_meshid_ie(skb, sdata) || |
2483 | mesh_add_meshconf_ie(skb, sdata) || | 2493 | mesh_add_meshconf_ie(skb, sdata) || |
2494 | mesh_add_awake_window_ie(skb, sdata) || | ||
2484 | mesh_add_vendor_ies(skb, sdata)) { | 2495 | mesh_add_vendor_ies(skb, sdata)) { |
2485 | pr_err("o11s: couldn't add ies!\n"); | 2496 | pr_err("o11s: couldn't add ies!\n"); |
2486 | goto out; | 2497 | goto out; |
@@ -2734,6 +2745,8 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw, | |||
2734 | goto out; | 2745 | goto out; |
2735 | 2746 | ||
2736 | ps = &sdata->u.ap.ps; | 2747 | ps = &sdata->u.ap.ps; |
2748 | } else if (ieee80211_vif_is_mesh(&sdata->vif)) { | ||
2749 | ps = &sdata->u.mesh.ps; | ||
2737 | } else { | 2750 | } else { |
2738 | goto out; | 2751 | goto out; |
2739 | } | 2752 | } |
diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 139ad9b66c39..6cb71a350edd 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c | |||
@@ -805,6 +805,10 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len, | |||
805 | elems->peering = pos; | 805 | elems->peering = pos; |
806 | elems->peering_len = elen; | 806 | elems->peering_len = elen; |
807 | break; | 807 | break; |
808 | case WLAN_EID_MESH_AWAKE_WINDOW: | ||
809 | if (elen >= 2) | ||
810 | elems->awake_window = (void *)pos; | ||
811 | break; | ||
808 | case WLAN_EID_PREQ: | 812 | case WLAN_EID_PREQ: |
809 | elems->preq = pos; | 813 | elems->preq = pos; |
810 | elems->preq_len = elen; | 814 | elems->preq_len = elen; |
diff --git a/net/mac80211/wme.c b/net/mac80211/wme.c index 906f00cd6d2f..afba19cb6f87 100644 --- a/net/mac80211/wme.c +++ b/net/mac80211/wme.c | |||
@@ -191,6 +191,15 @@ void ieee80211_set_qos_hdr(struct ieee80211_sub_if_data *sdata, | |||
191 | 191 | ||
192 | /* qos header is 2 bytes */ | 192 | /* qos header is 2 bytes */ |
193 | *p++ = ack_policy | tid; | 193 | *p++ = ack_policy | tid; |
194 | *p = ieee80211_vif_is_mesh(&sdata->vif) ? | 194 | if (ieee80211_vif_is_mesh(&sdata->vif)) { |
195 | (IEEE80211_QOS_CTL_MESH_CONTROL_PRESENT >> 8) : 0; | 195 | /* preserve RSPI and Mesh PS Level bit */ |
196 | *p &= ((IEEE80211_QOS_CTL_RSPI | | ||
197 | IEEE80211_QOS_CTL_MESH_PS_LEVEL) >> 8); | ||
198 | |||
199 | /* Nulls don't have a mesh header (frame body) */ | ||
200 | if (!ieee80211_is_qos_nullfunc(hdr->frame_control)) | ||
201 | *p |= (IEEE80211_QOS_CTL_MESH_CONTROL_PRESENT >> 8); | ||
202 | } else { | ||
203 | *p = 0; | ||
204 | } | ||
196 | } | 205 | } |