diff options
| -rw-r--r-- | include/net/mac80211.h | 15 | ||||
| -rw-r--r-- | net/mac80211/cfg.c | 15 | ||||
| -rw-r--r-- | net/mac80211/chan.c | 90 | ||||
| -rw-r--r-- | net/mac80211/debugfs_netdev.c | 2 | ||||
| -rw-r--r-- | net/mac80211/ibss.c | 3 | ||||
| -rw-r--r-- | net/mac80211/ieee80211_i.h | 20 | ||||
| -rw-r--r-- | net/mac80211/iface.c | 10 | ||||
| -rw-r--r-- | net/mac80211/main.c | 18 | ||||
| -rw-r--r-- | net/mac80211/mlme.c | 28 | ||||
| -rw-r--r-- | net/mac80211/status.c | 15 | ||||
| -rw-r--r-- | net/mac80211/trace.h | 13 | ||||
| -rw-r--r-- | net/mac80211/util.c | 82 |
12 files changed, 219 insertions, 92 deletions
diff --git a/include/net/mac80211.h b/include/net/mac80211.h index d9d2119f0828..3560881d17ee 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h | |||
| @@ -146,9 +146,11 @@ struct ieee80211_low_level_stats { | |||
| 146 | /** | 146 | /** |
| 147 | * enum ieee80211_chanctx_change - change flag for channel context | 147 | * enum ieee80211_chanctx_change - change flag for channel context |
| 148 | * @IEEE80211_CHANCTX_CHANGE_CHANNEL_TYPE: The channel type was changed | 148 | * @IEEE80211_CHANCTX_CHANGE_CHANNEL_TYPE: The channel type was changed |
| 149 | * @IEEE80211_CHANCTX_CHANGE_RX_CHAINS: The number of RX chains changed | ||
| 149 | */ | 150 | */ |
| 150 | enum ieee80211_chanctx_change { | 151 | enum ieee80211_chanctx_change { |
| 151 | IEEE80211_CHANCTX_CHANGE_CHANNEL_TYPE = BIT(0), | 152 | IEEE80211_CHANCTX_CHANGE_CHANNEL_TYPE = BIT(0), |
| 153 | IEEE80211_CHANCTX_CHANGE_RX_CHAINS = BIT(1), | ||
| 152 | }; | 154 | }; |
| 153 | 155 | ||
| 154 | /** | 156 | /** |
| @@ -159,6 +161,11 @@ enum ieee80211_chanctx_change { | |||
| 159 | * | 161 | * |
| 160 | * @channel: the channel to tune to | 162 | * @channel: the channel to tune to |
| 161 | * @channel_type: the channel (HT) type | 163 | * @channel_type: the channel (HT) type |
| 164 | * @rx_chains_static: The number of RX chains that must always be | ||
| 165 | * active on the channel to receive MIMO transmissions | ||
| 166 | * @rx_chains_dynamic: The number of RX chains that must be enabled | ||
| 167 | * after RTS/CTS handshake to receive SMPS MIMO transmissions; | ||
| 168 | * this will always be >= @rx_chains_always. | ||
| 162 | * @drv_priv: data area for driver use, will always be aligned to | 169 | * @drv_priv: data area for driver use, will always be aligned to |
| 163 | * sizeof(void *), size is determined in hw information. | 170 | * sizeof(void *), size is determined in hw information. |
| 164 | */ | 171 | */ |
| @@ -166,6 +173,8 @@ struct ieee80211_chanctx_conf { | |||
| 166 | struct ieee80211_channel *channel; | 173 | struct ieee80211_channel *channel; |
| 167 | enum nl80211_channel_type channel_type; | 174 | enum nl80211_channel_type channel_type; |
| 168 | 175 | ||
| 176 | u8 rx_chains_static, rx_chains_dynamic; | ||
| 177 | |||
| 169 | u8 drv_priv[0] __attribute__((__aligned__(sizeof(void *)))); | 178 | u8 drv_priv[0] __attribute__((__aligned__(sizeof(void *)))); |
| 170 | }; | 179 | }; |
| 171 | 180 | ||
| @@ -820,6 +829,8 @@ enum ieee80211_conf_flags { | |||
| 820 | * @IEEE80211_CONF_CHANGE_RETRY_LIMITS: retry limits changed | 829 | * @IEEE80211_CONF_CHANGE_RETRY_LIMITS: retry limits changed |
| 821 | * @IEEE80211_CONF_CHANGE_IDLE: Idle flag changed | 830 | * @IEEE80211_CONF_CHANGE_IDLE: Idle flag changed |
| 822 | * @IEEE80211_CONF_CHANGE_SMPS: Spatial multiplexing powersave mode changed | 831 | * @IEEE80211_CONF_CHANGE_SMPS: Spatial multiplexing powersave mode changed |
| 832 | * Note that this is only valid if channel contexts are not used, | ||
| 833 | * otherwise each channel context has the number of chains listed. | ||
| 823 | */ | 834 | */ |
| 824 | enum ieee80211_conf_changed { | 835 | enum ieee80211_conf_changed { |
| 825 | IEEE80211_CONF_CHANGE_SMPS = BIT(1), | 836 | IEEE80211_CONF_CHANGE_SMPS = BIT(1), |
| @@ -885,7 +896,9 @@ enum ieee80211_smps_mode { | |||
| 885 | * | 896 | * |
| 886 | * @smps_mode: spatial multiplexing powersave mode; note that | 897 | * @smps_mode: spatial multiplexing powersave mode; note that |
| 887 | * %IEEE80211_SMPS_STATIC is used when the device is not | 898 | * %IEEE80211_SMPS_STATIC is used when the device is not |
| 888 | * configured for an HT channel | 899 | * configured for an HT channel. |
| 900 | * Note that this is only valid if channel contexts are not used, | ||
| 901 | * otherwise each channel context has the number of chains listed. | ||
| 889 | */ | 902 | */ |
| 890 | struct ieee80211_conf { | 903 | struct ieee80211_conf { |
| 891 | u32 flags; | 904 | u32 flags; |
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 09c90627fd19..03216b0408c7 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c | |||
| @@ -884,6 +884,10 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev, | |||
| 884 | if (old) | 884 | if (old) |
| 885 | return -EALREADY; | 885 | return -EALREADY; |
| 886 | 886 | ||
| 887 | /* TODO: make hostapd tell us what it wants */ | ||
| 888 | sdata->smps_mode = IEEE80211_SMPS_OFF; | ||
| 889 | sdata->needed_rx_chains = sdata->local->rx_chains; | ||
| 890 | |||
| 887 | err = ieee80211_vif_use_channel(sdata, params->channel, | 891 | err = ieee80211_vif_use_channel(sdata, params->channel, |
| 888 | params->channel_type, | 892 | params->channel_type, |
| 889 | IEEE80211_CHANCTX_SHARED); | 893 | IEEE80211_CHANCTX_SHARED); |
| @@ -1673,6 +1677,10 @@ static int ieee80211_join_mesh(struct wiphy *wiphy, struct net_device *dev, | |||
| 1673 | if (err) | 1677 | if (err) |
| 1674 | return err; | 1678 | return err; |
| 1675 | 1679 | ||
| 1680 | /* can mesh use other SMPS modes? */ | ||
| 1681 | sdata->smps_mode = IEEE80211_SMPS_OFF; | ||
| 1682 | sdata->needed_rx_chains = sdata->local->rx_chains; | ||
| 1683 | |||
| 1676 | err = ieee80211_vif_use_channel(sdata, setup->channel, | 1684 | err = ieee80211_vif_use_channel(sdata, setup->channel, |
| 1677 | setup->channel_type, | 1685 | setup->channel_type, |
| 1678 | IEEE80211_CHANCTX_SHARED); | 1686 | IEEE80211_CHANCTX_SHARED); |
| @@ -2052,13 +2060,12 @@ int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata, | |||
| 2052 | 2060 | ||
| 2053 | /* | 2061 | /* |
| 2054 | * If not associated, or current association is not an HT | 2062 | * If not associated, or current association is not an HT |
| 2055 | * association, there's no need to send an action frame. | 2063 | * association, there's no need to do anything, just store |
| 2064 | * the new value until we associate. | ||
| 2056 | */ | 2065 | */ |
| 2057 | if (!sdata->u.mgd.associated || | 2066 | if (!sdata->u.mgd.associated || |
| 2058 | sdata->vif.bss_conf.channel_type == NL80211_CHAN_NO_HT) { | 2067 | sdata->vif.bss_conf.channel_type == NL80211_CHAN_NO_HT) |
| 2059 | ieee80211_recalc_smps(sdata->local); | ||
| 2060 | return 0; | 2068 | return 0; |
| 2061 | } | ||
| 2062 | 2069 | ||
| 2063 | ap = sdata->u.mgd.associated->bssid; | 2070 | ap = sdata->u.mgd.associated->bssid; |
| 2064 | 2071 | ||
diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c index 41e1aa69f7aa..bfaa486d928c 100644 --- a/net/mac80211/chan.c +++ b/net/mac80211/chan.c | |||
| @@ -118,6 +118,8 @@ ieee80211_new_chanctx(struct ieee80211_local *local, | |||
| 118 | 118 | ||
| 119 | ctx->conf.channel = channel; | 119 | ctx->conf.channel = channel; |
| 120 | ctx->conf.channel_type = channel_type; | 120 | ctx->conf.channel_type = channel_type; |
| 121 | ctx->conf.rx_chains_static = 1; | ||
| 122 | ctx->conf.rx_chains_dynamic = 1; | ||
| 121 | ctx->mode = mode; | 123 | ctx->mode = mode; |
| 122 | 124 | ||
| 123 | if (!local->use_chanctx) { | 125 | if (!local->use_chanctx) { |
| @@ -222,8 +224,10 @@ static void ieee80211_unassign_vif_chanctx(struct ieee80211_sub_if_data *sdata, | |||
| 222 | 224 | ||
| 223 | drv_unassign_vif_chanctx(local, sdata, ctx); | 225 | drv_unassign_vif_chanctx(local, sdata, ctx); |
| 224 | 226 | ||
| 225 | if (ctx->refcount > 0) | 227 | if (ctx->refcount > 0) { |
| 226 | ieee80211_recalc_chanctx_chantype(sdata->local, ctx); | 228 | ieee80211_recalc_chanctx_chantype(sdata->local, ctx); |
| 229 | ieee80211_recalc_smps_chanctx(local, ctx); | ||
| 230 | } | ||
| 227 | } | 231 | } |
| 228 | 232 | ||
| 229 | static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata) | 233 | static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata) |
| @@ -246,6 +250,89 @@ static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata) | |||
| 246 | ieee80211_free_chanctx(local, ctx); | 250 | ieee80211_free_chanctx(local, ctx); |
| 247 | } | 251 | } |
| 248 | 252 | ||
| 253 | void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local, | ||
| 254 | struct ieee80211_chanctx *chanctx) | ||
| 255 | { | ||
| 256 | struct ieee80211_sub_if_data *sdata; | ||
| 257 | u8 rx_chains_static, rx_chains_dynamic; | ||
| 258 | |||
| 259 | lockdep_assert_held(&local->chanctx_mtx); | ||
| 260 | |||
| 261 | rx_chains_static = 1; | ||
| 262 | rx_chains_dynamic = 1; | ||
| 263 | |||
| 264 | rcu_read_lock(); | ||
| 265 | list_for_each_entry_rcu(sdata, &local->interfaces, list) { | ||
| 266 | u8 needed_static, needed_dynamic; | ||
| 267 | |||
| 268 | if (!ieee80211_sdata_running(sdata)) | ||
| 269 | continue; | ||
| 270 | |||
| 271 | if (rcu_access_pointer(sdata->vif.chanctx_conf) != | ||
| 272 | &chanctx->conf) | ||
| 273 | continue; | ||
| 274 | |||
| 275 | switch (sdata->vif.type) { | ||
| 276 | case NL80211_IFTYPE_P2P_DEVICE: | ||
| 277 | continue; | ||
| 278 | case NL80211_IFTYPE_STATION: | ||
| 279 | if (!sdata->u.mgd.associated) | ||
| 280 | continue; | ||
| 281 | break; | ||
| 282 | case NL80211_IFTYPE_AP_VLAN: | ||
| 283 | continue; | ||
| 284 | case NL80211_IFTYPE_AP: | ||
| 285 | case NL80211_IFTYPE_ADHOC: | ||
| 286 | case NL80211_IFTYPE_WDS: | ||
| 287 | case NL80211_IFTYPE_MESH_POINT: | ||
| 288 | break; | ||
| 289 | default: | ||
| 290 | WARN_ON_ONCE(1); | ||
| 291 | } | ||
| 292 | |||
| 293 | switch (sdata->smps_mode) { | ||
| 294 | default: | ||
| 295 | WARN_ONCE(1, "Invalid SMPS mode %d\n", | ||
| 296 | sdata->smps_mode); | ||
| 297 | /* fall through */ | ||
| 298 | case IEEE80211_SMPS_OFF: | ||
| 299 | needed_static = sdata->needed_rx_chains; | ||
| 300 | needed_dynamic = sdata->needed_rx_chains; | ||
| 301 | break; | ||
| 302 | case IEEE80211_SMPS_DYNAMIC: | ||
| 303 | needed_static = 1; | ||
| 304 | needed_dynamic = sdata->needed_rx_chains; | ||
| 305 | break; | ||
| 306 | case IEEE80211_SMPS_STATIC: | ||
| 307 | needed_static = 1; | ||
| 308 | needed_dynamic = 1; | ||
| 309 | break; | ||
| 310 | } | ||
| 311 | |||
| 312 | rx_chains_static = max(rx_chains_static, needed_static); | ||
| 313 | rx_chains_dynamic = max(rx_chains_dynamic, needed_dynamic); | ||
| 314 | } | ||
| 315 | rcu_read_unlock(); | ||
| 316 | |||
| 317 | if (!local->use_chanctx) { | ||
| 318 | if (rx_chains_static > 1) | ||
| 319 | local->smps_mode = IEEE80211_SMPS_OFF; | ||
| 320 | else if (rx_chains_dynamic > 1) | ||
| 321 | local->smps_mode = IEEE80211_SMPS_DYNAMIC; | ||
| 322 | else | ||
| 323 | local->smps_mode = IEEE80211_SMPS_STATIC; | ||
| 324 | ieee80211_hw_config(local, 0); | ||
| 325 | } | ||
| 326 | |||
| 327 | if (rx_chains_static == chanctx->conf.rx_chains_static && | ||
| 328 | rx_chains_dynamic == chanctx->conf.rx_chains_dynamic) | ||
| 329 | return; | ||
| 330 | |||
| 331 | chanctx->conf.rx_chains_static = rx_chains_static; | ||
| 332 | chanctx->conf.rx_chains_dynamic = rx_chains_dynamic; | ||
| 333 | drv_change_chanctx(local, chanctx, IEEE80211_CHANCTX_CHANGE_RX_CHAINS); | ||
| 334 | } | ||
| 335 | |||
| 249 | int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata, | 336 | int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata, |
| 250 | struct ieee80211_channel *channel, | 337 | struct ieee80211_channel *channel, |
| 251 | enum nl80211_channel_type channel_type, | 338 | enum nl80211_channel_type channel_type, |
| @@ -278,6 +365,7 @@ int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata, | |||
| 278 | goto out; | 365 | goto out; |
| 279 | } | 366 | } |
| 280 | 367 | ||
| 368 | ieee80211_recalc_smps_chanctx(local, ctx); | ||
| 281 | out: | 369 | out: |
| 282 | mutex_unlock(&local->chanctx_mtx); | 370 | mutex_unlock(&local->chanctx_mtx); |
| 283 | return ret; | 371 | return ret; |
diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c index 6d5aec9418ee..34e173976573 100644 --- a/net/mac80211/debugfs_netdev.c +++ b/net/mac80211/debugfs_netdev.c | |||
| @@ -217,7 +217,7 @@ static ssize_t ieee80211_if_fmt_smps(const struct ieee80211_sub_if_data *sdata, | |||
| 217 | 217 | ||
| 218 | return snprintf(buf, buflen, "request: %s\nused: %s\n", | 218 | return snprintf(buf, buflen, "request: %s\nused: %s\n", |
| 219 | smps_modes[sdata->u.mgd.req_smps], | 219 | smps_modes[sdata->u.mgd.req_smps], |
| 220 | smps_modes[sdata->u.mgd.ap_smps]); | 220 | smps_modes[sdata->smps_mode]); |
| 221 | } | 221 | } |
| 222 | 222 | ||
| 223 | static ssize_t ieee80211_if_parse_smps(struct ieee80211_sub_if_data *sdata, | 223 | static ssize_t ieee80211_if_parse_smps(struct ieee80211_sub_if_data *sdata, |
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 34d9235117d9..291c9e07f1bd 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c | |||
| @@ -1132,6 +1132,9 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata, | |||
| 1132 | changed |= BSS_CHANGED_HT; | 1132 | changed |= BSS_CHANGED_HT; |
| 1133 | ieee80211_bss_info_change_notify(sdata, changed); | 1133 | ieee80211_bss_info_change_notify(sdata, changed); |
| 1134 | 1134 | ||
| 1135 | sdata->smps_mode = IEEE80211_SMPS_OFF; | ||
| 1136 | sdata->needed_rx_chains = sdata->local->rx_chains; | ||
| 1137 | |||
| 1135 | ieee80211_queue_work(&sdata->local->hw, &sdata->work); | 1138 | ieee80211_queue_work(&sdata->local->hw, &sdata->work); |
| 1136 | 1139 | ||
| 1137 | return 0; | 1140 | return 0; |
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 6660118b46b3..132577d22928 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h | |||
| @@ -433,7 +433,6 @@ struct ieee80211_if_managed { | |||
| 433 | bool powersave; /* powersave requested for this iface */ | 433 | bool powersave; /* powersave requested for this iface */ |
| 434 | bool broken_ap; /* AP is broken -- turn off powersave */ | 434 | bool broken_ap; /* AP is broken -- turn off powersave */ |
| 435 | enum ieee80211_smps_mode req_smps, /* requested smps mode */ | 435 | enum ieee80211_smps_mode req_smps, /* requested smps mode */ |
| 436 | ap_smps, /* smps mode AP thinks we're in */ | ||
| 437 | driver_smps_mode; /* smps mode request */ | 436 | driver_smps_mode; /* smps mode request */ |
| 438 | 437 | ||
| 439 | struct work_struct request_smps_work; | 438 | struct work_struct request_smps_work; |
| @@ -728,11 +727,17 @@ struct ieee80211_sub_if_data { | |||
| 728 | 727 | ||
| 729 | struct ieee80211_tx_queue_params tx_conf[IEEE80211_NUM_ACS]; | 728 | struct ieee80211_tx_queue_params tx_conf[IEEE80211_NUM_ACS]; |
| 730 | 729 | ||
| 730 | /* used to reconfigure hardware SM PS */ | ||
| 731 | struct work_struct recalc_smps; | ||
| 732 | |||
| 731 | struct work_struct work; | 733 | struct work_struct work; |
| 732 | struct sk_buff_head skb_queue; | 734 | struct sk_buff_head skb_queue; |
| 733 | 735 | ||
| 734 | bool arp_filter_state; | 736 | bool arp_filter_state; |
| 735 | 737 | ||
| 738 | u8 needed_rx_chains; | ||
| 739 | enum ieee80211_smps_mode smps_mode; | ||
| 740 | |||
| 736 | /* | 741 | /* |
| 737 | * AP this belongs to: self in AP mode and | 742 | * AP this belongs to: self in AP mode and |
| 738 | * corresponding AP in VLAN mode, NULL for | 743 | * corresponding AP in VLAN mode, NULL for |
| @@ -905,9 +910,6 @@ struct ieee80211_local { | |||
| 905 | /* used for uploading changed mc list */ | 910 | /* used for uploading changed mc list */ |
| 906 | struct work_struct reconfig_filter; | 911 | struct work_struct reconfig_filter; |
| 907 | 912 | ||
| 908 | /* used to reconfigure hardware SM PS */ | ||
| 909 | struct work_struct recalc_smps; | ||
| 910 | |||
| 911 | /* aggregated multicast list */ | 913 | /* aggregated multicast list */ |
| 912 | struct netdev_hw_addr_list mc_list; | 914 | struct netdev_hw_addr_list mc_list; |
| 913 | 915 | ||
| @@ -944,6 +946,9 @@ struct ieee80211_local { | |||
| 944 | /* wowlan is enabled -- don't reconfig on resume */ | 946 | /* wowlan is enabled -- don't reconfig on resume */ |
| 945 | bool wowlan; | 947 | bool wowlan; |
| 946 | 948 | ||
| 949 | /* number of RX chains the hardware has */ | ||
| 950 | u8 rx_chains; | ||
| 951 | |||
| 947 | int tx_headroom; /* required headroom for hardware/radiotap */ | 952 | int tx_headroom; /* required headroom for hardware/radiotap */ |
| 948 | 953 | ||
| 949 | /* Tasklet and skb queue to process calls from IRQ mode. All frames | 954 | /* Tasklet and skb queue to process calls from IRQ mode. All frames |
| @@ -1408,6 +1413,8 @@ void ieee80211_ba_session_work(struct work_struct *work); | |||
| 1408 | void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid); | 1413 | void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid); |
| 1409 | void ieee80211_release_reorder_timeout(struct sta_info *sta, int tid); | 1414 | void ieee80211_release_reorder_timeout(struct sta_info *sta, int tid); |
| 1410 | 1415 | ||
| 1416 | u8 ieee80211_mcs_to_chains(const struct ieee80211_mcs_info *mcs); | ||
| 1417 | |||
| 1411 | /* Spectrum management */ | 1418 | /* Spectrum management */ |
| 1412 | void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata, | 1419 | void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata, |
| 1413 | struct ieee80211_mgmt *mgmt, | 1420 | struct ieee80211_mgmt *mgmt, |
| @@ -1554,7 +1561,7 @@ u32 ieee80211_sta_get_rates(struct ieee80211_local *local, | |||
| 1554 | enum ieee80211_band band, u32 *basic_rates); | 1561 | enum ieee80211_band band, u32 *basic_rates); |
| 1555 | int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata, | 1562 | int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata, |
| 1556 | enum ieee80211_smps_mode smps_mode); | 1563 | enum ieee80211_smps_mode smps_mode); |
| 1557 | void ieee80211_recalc_smps(struct ieee80211_local *local); | 1564 | void ieee80211_recalc_smps(struct ieee80211_sub_if_data *sdata); |
| 1558 | 1565 | ||
| 1559 | size_t ieee80211_ie_split(const u8 *ies, size_t ielen, | 1566 | size_t ieee80211_ie_split(const u8 *ies, size_t ielen, |
| 1560 | const u8 *ids, int n_ids, size_t offset); | 1567 | const u8 *ids, int n_ids, size_t offset); |
| @@ -1585,6 +1592,9 @@ ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata, | |||
| 1585 | enum ieee80211_chanctx_mode mode); | 1592 | enum ieee80211_chanctx_mode mode); |
| 1586 | void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata); | 1593 | void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata); |
| 1587 | 1594 | ||
| 1595 | void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local, | ||
| 1596 | struct ieee80211_chanctx *chanctx); | ||
| 1597 | |||
| 1588 | #ifdef CONFIG_MAC80211_NOINLINE | 1598 | #ifdef CONFIG_MAC80211_NOINLINE |
| 1589 | #define debug_noinline noinline | 1599 | #define debug_noinline noinline |
| 1590 | #else | 1600 | #else |
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 7cb8382b19e5..99f2b19c8f0d 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c | |||
| @@ -739,6 +739,8 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, | |||
| 739 | del_timer_sync(&local->dynamic_ps_timer); | 739 | del_timer_sync(&local->dynamic_ps_timer); |
| 740 | cancel_work_sync(&local->dynamic_ps_enable_work); | 740 | cancel_work_sync(&local->dynamic_ps_enable_work); |
| 741 | 741 | ||
| 742 | cancel_work_sync(&sdata->recalc_smps); | ||
| 743 | |||
| 742 | /* APs need special treatment */ | 744 | /* APs need special treatment */ |
| 743 | if (sdata->vif.type == NL80211_IFTYPE_AP) { | 745 | if (sdata->vif.type == NL80211_IFTYPE_AP) { |
| 744 | struct ieee80211_sub_if_data *vlan, *tmpsdata; | 746 | struct ieee80211_sub_if_data *vlan, *tmpsdata; |
| @@ -1125,6 +1127,13 @@ static void ieee80211_iface_work(struct work_struct *work) | |||
| 1125 | } | 1127 | } |
| 1126 | } | 1128 | } |
| 1127 | 1129 | ||
| 1130 | static void ieee80211_recalc_smps_work(struct work_struct *work) | ||
| 1131 | { | ||
| 1132 | struct ieee80211_sub_if_data *sdata = | ||
| 1133 | container_of(work, struct ieee80211_sub_if_data, recalc_smps); | ||
| 1134 | |||
| 1135 | ieee80211_recalc_smps(sdata); | ||
| 1136 | } | ||
| 1128 | 1137 | ||
| 1129 | /* | 1138 | /* |
| 1130 | * Helper function to initialise an interface to a specific type. | 1139 | * Helper function to initialise an interface to a specific type. |
| @@ -1153,6 +1162,7 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata, | |||
| 1153 | 1162 | ||
| 1154 | skb_queue_head_init(&sdata->skb_queue); | 1163 | skb_queue_head_init(&sdata->skb_queue); |
| 1155 | INIT_WORK(&sdata->work, ieee80211_iface_work); | 1164 | INIT_WORK(&sdata->work, ieee80211_iface_work); |
| 1165 | INIT_WORK(&sdata->recalc_smps, ieee80211_recalc_smps_work); | ||
| 1156 | 1166 | ||
| 1157 | switch (type) { | 1167 | switch (type) { |
| 1158 | case NL80211_IFTYPE_P2P_GO: | 1168 | case NL80211_IFTYPE_P2P_GO: |
diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 9cb6280aa2f2..2c8969b67851 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c | |||
| @@ -372,14 +372,6 @@ void ieee80211_restart_hw(struct ieee80211_hw *hw) | |||
| 372 | } | 372 | } |
| 373 | EXPORT_SYMBOL(ieee80211_restart_hw); | 373 | EXPORT_SYMBOL(ieee80211_restart_hw); |
| 374 | 374 | ||
| 375 | static void ieee80211_recalc_smps_work(struct work_struct *work) | ||
| 376 | { | ||
| 377 | struct ieee80211_local *local = | ||
| 378 | container_of(work, struct ieee80211_local, recalc_smps); | ||
| 379 | |||
| 380 | ieee80211_recalc_smps(local); | ||
| 381 | } | ||
| 382 | |||
| 383 | #ifdef CONFIG_INET | 375 | #ifdef CONFIG_INET |
| 384 | static int ieee80211_ifa_changed(struct notifier_block *nb, | 376 | static int ieee80211_ifa_changed(struct notifier_block *nb, |
| 385 | unsigned long data, void *arg) | 377 | unsigned long data, void *arg) |
| @@ -667,7 +659,6 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, | |||
| 667 | INIT_WORK(&local->restart_work, ieee80211_restart_work); | 659 | INIT_WORK(&local->restart_work, ieee80211_restart_work); |
| 668 | 660 | ||
| 669 | INIT_WORK(&local->reconfig_filter, ieee80211_reconfig_filter); | 661 | INIT_WORK(&local->reconfig_filter, ieee80211_reconfig_filter); |
| 670 | INIT_WORK(&local->recalc_smps, ieee80211_recalc_smps_work); | ||
| 671 | local->smps_mode = IEEE80211_SMPS_OFF; | 662 | local->smps_mode = IEEE80211_SMPS_OFF; |
| 672 | 663 | ||
| 673 | INIT_WORK(&local->dynamic_ps_enable_work, | 664 | INIT_WORK(&local->dynamic_ps_enable_work, |
| @@ -773,6 +764,8 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) | |||
| 773 | if (hw->max_report_rates == 0) | 764 | if (hw->max_report_rates == 0) |
| 774 | hw->max_report_rates = hw->max_rates; | 765 | hw->max_report_rates = hw->max_rates; |
| 775 | 766 | ||
| 767 | local->rx_chains = 1; | ||
| 768 | |||
| 776 | /* | 769 | /* |
| 777 | * generic code guarantees at least one band, | 770 | * generic code guarantees at least one band, |
| 778 | * set this very early because much code assumes | 771 | * set this very early because much code assumes |
| @@ -804,6 +797,13 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) | |||
| 804 | max_bitrates = sband->n_bitrates; | 797 | max_bitrates = sband->n_bitrates; |
| 805 | supp_ht = supp_ht || sband->ht_cap.ht_supported; | 798 | supp_ht = supp_ht || sband->ht_cap.ht_supported; |
| 806 | supp_vht = supp_vht || sband->vht_cap.vht_supported; | 799 | supp_vht = supp_vht || sband->vht_cap.vht_supported; |
| 800 | |||
| 801 | if (sband->ht_cap.ht_supported) | ||
| 802 | local->rx_chains = | ||
| 803 | max(ieee80211_mcs_to_chains(&sband->ht_cap.mcs), | ||
| 804 | local->rx_chains); | ||
| 805 | |||
| 806 | /* TODO: consider VHT for RX chains, hopefully it's the same */ | ||
| 807 | } | 807 | } |
| 808 | 808 | ||
| 809 | local->int_scan_req = kzalloc(sizeof(*local->int_scan_req) + | 809 | local->int_scan_req = kzalloc(sizeof(*local->int_scan_req) + |
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 4add50063161..f3f338541b01 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c | |||
| @@ -543,7 +543,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) | |||
| 543 | 543 | ||
| 544 | if (!(ifmgd->flags & IEEE80211_STA_DISABLE_11N)) | 544 | if (!(ifmgd->flags & IEEE80211_STA_DISABLE_11N)) |
| 545 | ieee80211_add_ht_ie(sdata, skb, assoc_data->ap_ht_param, | 545 | ieee80211_add_ht_ie(sdata, skb, assoc_data->ap_ht_param, |
| 546 | sband, chan, ifmgd->ap_smps); | 546 | sband, chan, sdata->smps_mode); |
| 547 | 547 | ||
| 548 | if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT)) | 548 | if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT)) |
| 549 | ieee80211_add_vht_ie(sdata, skb, sband); | 549 | ieee80211_add_vht_ie(sdata, skb, sband); |
| @@ -1392,7 +1392,7 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, | |||
| 1392 | ieee80211_recalc_ps(local, -1); | 1392 | ieee80211_recalc_ps(local, -1); |
| 1393 | mutex_unlock(&local->iflist_mtx); | 1393 | mutex_unlock(&local->iflist_mtx); |
| 1394 | 1394 | ||
| 1395 | ieee80211_recalc_smps(local); | 1395 | ieee80211_recalc_smps(sdata); |
| 1396 | ieee80211_recalc_ps_vif(sdata); | 1396 | ieee80211_recalc_ps_vif(sdata); |
| 1397 | 1397 | ||
| 1398 | netif_tx_start_all_queues(sdata->dev); | 1398 | netif_tx_start_all_queues(sdata->dev); |
| @@ -3157,6 +3157,10 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata, | |||
| 3157 | } | 3157 | } |
| 3158 | 3158 | ||
| 3159 | if (ht_oper) { | 3159 | if (ht_oper) { |
| 3160 | const u8 *ht_cap_ie; | ||
| 3161 | const struct ieee80211_ht_cap *ht_cap; | ||
| 3162 | u8 chains = 1; | ||
| 3163 | |||
| 3160 | channel_type = NL80211_CHAN_HT20; | 3164 | channel_type = NL80211_CHAN_HT20; |
| 3161 | 3165 | ||
| 3162 | if (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) { | 3166 | if (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) { |
| @@ -3170,8 +3174,22 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata, | |||
| 3170 | break; | 3174 | break; |
| 3171 | } | 3175 | } |
| 3172 | } | 3176 | } |
| 3177 | |||
| 3178 | ht_cap_ie = cfg80211_find_ie(WLAN_EID_HT_CAPABILITY, | ||
| 3179 | cbss->information_elements, | ||
| 3180 | cbss->len_information_elements); | ||
| 3181 | if (ht_cap_ie && ht_cap_ie[1] >= sizeof(*ht_cap)) { | ||
| 3182 | ht_cap = (void *)(ht_cap_ie + 2); | ||
| 3183 | chains = ieee80211_mcs_to_chains(&ht_cap->mcs); | ||
| 3184 | } | ||
| 3185 | sdata->needed_rx_chains = min(chains, local->rx_chains); | ||
| 3186 | } else { | ||
| 3187 | sdata->needed_rx_chains = 1; | ||
| 3173 | } | 3188 | } |
| 3174 | 3189 | ||
| 3190 | /* will change later if needed */ | ||
| 3191 | sdata->smps_mode = IEEE80211_SMPS_OFF; | ||
| 3192 | |||
| 3175 | ieee80211_vif_release_channel(sdata); | 3193 | ieee80211_vif_release_channel(sdata); |
| 3176 | return ieee80211_vif_use_channel(sdata, cbss->channel, channel_type, | 3194 | return ieee80211_vif_use_channel(sdata, cbss->channel, channel_type, |
| 3177 | IEEE80211_CHANCTX_SHARED); | 3195 | IEEE80211_CHANCTX_SHARED); |
| @@ -3485,11 +3503,11 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, | |||
| 3485 | 3503 | ||
| 3486 | if (ifmgd->req_smps == IEEE80211_SMPS_AUTOMATIC) { | 3504 | if (ifmgd->req_smps == IEEE80211_SMPS_AUTOMATIC) { |
| 3487 | if (ifmgd->powersave) | 3505 | if (ifmgd->powersave) |
| 3488 | ifmgd->ap_smps = IEEE80211_SMPS_DYNAMIC; | 3506 | sdata->smps_mode = IEEE80211_SMPS_DYNAMIC; |
| 3489 | else | 3507 | else |
| 3490 | ifmgd->ap_smps = IEEE80211_SMPS_OFF; | 3508 | sdata->smps_mode = IEEE80211_SMPS_OFF; |
| 3491 | } else | 3509 | } else |
| 3492 | ifmgd->ap_smps = ifmgd->req_smps; | 3510 | sdata->smps_mode = ifmgd->req_smps; |
| 3493 | 3511 | ||
| 3494 | assoc_data->capability = req->bss->capability; | 3512 | assoc_data->capability = req->bss->capability; |
| 3495 | assoc_data->wmm = bss->wmm_used && | 3513 | assoc_data->wmm = bss->wmm_used && |
diff --git a/net/mac80211/status.c b/net/mac80211/status.c index 3af0cc4130f1..21fa5c72ea14 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c | |||
| @@ -189,30 +189,31 @@ static void ieee80211_frame_acked(struct sta_info *sta, struct sk_buff *skb) | |||
| 189 | } | 189 | } |
| 190 | 190 | ||
| 191 | if (ieee80211_is_action(mgmt->frame_control) && | 191 | if (ieee80211_is_action(mgmt->frame_control) && |
| 192 | sdata->vif.type == NL80211_IFTYPE_STATION && | ||
| 193 | mgmt->u.action.category == WLAN_CATEGORY_HT && | 192 | mgmt->u.action.category == WLAN_CATEGORY_HT && |
| 194 | mgmt->u.action.u.ht_smps.action == WLAN_HT_ACTION_SMPS) { | 193 | mgmt->u.action.u.ht_smps.action == WLAN_HT_ACTION_SMPS && |
| 194 | sdata->vif.type == NL80211_IFTYPE_STATION && | ||
| 195 | ieee80211_sdata_running(sdata)) { | ||
| 195 | /* | 196 | /* |
| 196 | * This update looks racy, but isn't -- if we come | 197 | * This update looks racy, but isn't -- if we come |
| 197 | * here we've definitely got a station that we're | 198 | * here we've definitely got a station that we're |
| 198 | * talking to, and on a managed interface that can | 199 | * talking to, and on a managed interface that can |
| 199 | * only be the AP. And the only other place updating | 200 | * only be the AP. And the only other place updating |
| 200 | * this variable is before we're associated. | 201 | * this variable in managed mode is before association. |
| 201 | */ | 202 | */ |
| 202 | switch (mgmt->u.action.u.ht_smps.smps_control) { | 203 | switch (mgmt->u.action.u.ht_smps.smps_control) { |
| 203 | case WLAN_HT_SMPS_CONTROL_DYNAMIC: | 204 | case WLAN_HT_SMPS_CONTROL_DYNAMIC: |
| 204 | sta->sdata->u.mgd.ap_smps = IEEE80211_SMPS_DYNAMIC; | 205 | sdata->smps_mode = IEEE80211_SMPS_DYNAMIC; |
| 205 | break; | 206 | break; |
| 206 | case WLAN_HT_SMPS_CONTROL_STATIC: | 207 | case WLAN_HT_SMPS_CONTROL_STATIC: |
| 207 | sta->sdata->u.mgd.ap_smps = IEEE80211_SMPS_STATIC; | 208 | sdata->smps_mode = IEEE80211_SMPS_STATIC; |
| 208 | break; | 209 | break; |
| 209 | case WLAN_HT_SMPS_CONTROL_DISABLED: | 210 | case WLAN_HT_SMPS_CONTROL_DISABLED: |
| 210 | default: /* shouldn't happen since we don't send that */ | 211 | default: /* shouldn't happen since we don't send that */ |
| 211 | sta->sdata->u.mgd.ap_smps = IEEE80211_SMPS_OFF; | 212 | sdata->smps_mode = IEEE80211_SMPS_OFF; |
| 212 | break; | 213 | break; |
| 213 | } | 214 | } |
| 214 | 215 | ||
| 215 | ieee80211_queue_work(&local->hw, &local->recalc_smps); | 216 | ieee80211_queue_work(&local->hw, &sdata->recalc_smps); |
| 216 | } | 217 | } |
| 217 | } | 218 | } |
| 218 | 219 | ||
diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h index a3f5fe2a84a8..629364705f7b 100644 --- a/net/mac80211/trace.h +++ b/net/mac80211/trace.h | |||
| @@ -29,11 +29,16 @@ | |||
| 29 | #define VIF_PR_ARG __get_str(vif_name), __entry->vif_type, __entry->p2p ? "/p2p" : "" | 29 | #define VIF_PR_ARG __get_str(vif_name), __entry->vif_type, __entry->p2p ? "/p2p" : "" |
| 30 | 30 | ||
| 31 | #define CHANCTX_ENTRY __field(int, freq) \ | 31 | #define CHANCTX_ENTRY __field(int, freq) \ |
| 32 | __field(int, chantype) | 32 | __field(int, chantype) \ |
| 33 | __field(u8, rx_chains_static) \ | ||
| 34 | __field(u8, rx_chains_dynamic) | ||
| 33 | #define CHANCTX_ASSIGN __entry->freq = ctx->conf.channel->center_freq; \ | 35 | #define CHANCTX_ASSIGN __entry->freq = ctx->conf.channel->center_freq; \ |
| 34 | __entry->chantype = ctx->conf.channel_type | 36 | __entry->chantype = ctx->conf.channel_type; \ |
| 35 | #define CHANCTX_PR_FMT " freq:%d MHz chantype:%d" | 37 | __entry->rx_chains_static = ctx->conf.rx_chains_static; \ |
| 36 | #define CHANCTX_PR_ARG __entry->freq, __entry->chantype | 38 | __entry->rx_chains_dynamic = ctx->conf.rx_chains_dynamic |
| 39 | #define CHANCTX_PR_FMT " freq:%d MHz chantype:%d chains:%d/%d" | ||
| 40 | #define CHANCTX_PR_ARG __entry->freq, __entry->chantype, \ | ||
| 41 | __entry->rx_chains_static, __entry->rx_chains_dynamic | ||
| 37 | 42 | ||
| 38 | 43 | ||
| 39 | 44 | ||
diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 7d737071dedb..b732e219b107 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c | |||
| @@ -1618,68 +1618,24 @@ void ieee80211_resume_disconnect(struct ieee80211_vif *vif) | |||
| 1618 | } | 1618 | } |
| 1619 | EXPORT_SYMBOL_GPL(ieee80211_resume_disconnect); | 1619 | EXPORT_SYMBOL_GPL(ieee80211_resume_disconnect); |
| 1620 | 1620 | ||
| 1621 | static int check_mgd_smps(struct ieee80211_if_managed *ifmgd, | 1621 | void ieee80211_recalc_smps(struct ieee80211_sub_if_data *sdata) |
| 1622 | enum ieee80211_smps_mode *smps_mode) | ||
| 1623 | { | 1622 | { |
| 1624 | if (ifmgd->associated) { | 1623 | struct ieee80211_local *local = sdata->local; |
| 1625 | *smps_mode = ifmgd->ap_smps; | 1624 | struct ieee80211_chanctx_conf *chanctx_conf; |
| 1626 | 1625 | struct ieee80211_chanctx *chanctx; | |
| 1627 | if (*smps_mode == IEEE80211_SMPS_AUTOMATIC) { | ||
| 1628 | if (ifmgd->powersave) | ||
| 1629 | *smps_mode = IEEE80211_SMPS_DYNAMIC; | ||
| 1630 | else | ||
| 1631 | *smps_mode = IEEE80211_SMPS_OFF; | ||
| 1632 | } | ||
| 1633 | |||
| 1634 | return 1; | ||
| 1635 | } | ||
| 1636 | |||
| 1637 | return 0; | ||
| 1638 | } | ||
| 1639 | |||
| 1640 | void ieee80211_recalc_smps(struct ieee80211_local *local) | ||
| 1641 | { | ||
| 1642 | struct ieee80211_sub_if_data *sdata; | ||
| 1643 | enum ieee80211_smps_mode smps_mode = IEEE80211_SMPS_OFF; | ||
| 1644 | int count = 0; | ||
| 1645 | |||
| 1646 | mutex_lock(&local->iflist_mtx); | ||
| 1647 | |||
| 1648 | /* | ||
| 1649 | * This function could be improved to handle multiple | ||
| 1650 | * interfaces better, but right now it makes any | ||
| 1651 | * non-station interfaces force SM PS to be turned | ||
| 1652 | * off. If there are multiple station interfaces it | ||
| 1653 | * could also use the best possible mode, e.g. if | ||
| 1654 | * one is in static and the other in dynamic then | ||
| 1655 | * dynamic is ok. | ||
| 1656 | */ | ||
| 1657 | |||
| 1658 | list_for_each_entry(sdata, &local->interfaces, list) { | ||
| 1659 | if (!ieee80211_sdata_running(sdata)) | ||
| 1660 | continue; | ||
| 1661 | if (sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE) | ||
| 1662 | continue; | ||
| 1663 | if (sdata->vif.type != NL80211_IFTYPE_STATION) | ||
| 1664 | goto set; | ||
| 1665 | 1626 | ||
| 1666 | count += check_mgd_smps(&sdata->u.mgd, &smps_mode); | 1627 | mutex_lock(&local->chanctx_mtx); |
| 1667 | 1628 | ||
| 1668 | if (count > 1) { | 1629 | chanctx_conf = rcu_dereference_protected(sdata->vif.chanctx_conf, |
| 1669 | smps_mode = IEEE80211_SMPS_OFF; | 1630 | lockdep_is_held(&local->chanctx_mtx)); |
| 1670 | break; | ||
| 1671 | } | ||
| 1672 | } | ||
| 1673 | 1631 | ||
| 1674 | if (smps_mode == local->smps_mode) | 1632 | if (WARN_ON_ONCE(!chanctx_conf)) |
| 1675 | goto unlock; | 1633 | goto unlock; |
| 1676 | 1634 | ||
| 1677 | set: | 1635 | chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf); |
| 1678 | local->smps_mode = smps_mode; | 1636 | ieee80211_recalc_smps_chanctx(local, chanctx); |
| 1679 | /* changed flag is auto-detected for this */ | ||
| 1680 | ieee80211_hw_config(local, 0); | ||
| 1681 | unlock: | 1637 | unlock: |
| 1682 | mutex_unlock(&local->iflist_mtx); | 1638 | mutex_unlock(&local->chanctx_mtx); |
| 1683 | } | 1639 | } |
| 1684 | 1640 | ||
| 1685 | static bool ieee80211_id_in_list(const u8 *ids, int n_ids, u8 id) | 1641 | static bool ieee80211_id_in_list(const u8 *ids, int n_ids, u8 id) |
| @@ -1978,3 +1934,19 @@ int ieee80211_ave_rssi(struct ieee80211_vif *vif) | |||
| 1978 | return ifmgd->ave_beacon_signal; | 1934 | return ifmgd->ave_beacon_signal; |
| 1979 | } | 1935 | } |
| 1980 | EXPORT_SYMBOL_GPL(ieee80211_ave_rssi); | 1936 | EXPORT_SYMBOL_GPL(ieee80211_ave_rssi); |
| 1937 | |||
| 1938 | u8 ieee80211_mcs_to_chains(const struct ieee80211_mcs_info *mcs) | ||
| 1939 | { | ||
| 1940 | if (!mcs) | ||
| 1941 | return 1; | ||
| 1942 | |||
| 1943 | /* TODO: consider rx_highest */ | ||
| 1944 | |||
| 1945 | if (mcs->rx_mask[3]) | ||
| 1946 | return 4; | ||
| 1947 | if (mcs->rx_mask[2]) | ||
| 1948 | return 3; | ||
| 1949 | if (mcs->rx_mask[1]) | ||
| 1950 | return 2; | ||
| 1951 | return 1; | ||
| 1952 | } | ||
