diff options
-rw-r--r-- | drivers/net/wireless/mac80211_hwsim.c | 24 | ||||
-rw-r--r-- | include/linux/ieee80211.h | 25 | ||||
-rw-r--r-- | include/net/mac80211.h | 59 | ||||
-rw-r--r-- | net/mac80211/cfg.c | 49 | ||||
-rw-r--r-- | net/mac80211/debugfs_netdev.c | 111 | ||||
-rw-r--r-- | net/mac80211/driver-trace.h | 2 | ||||
-rw-r--r-- | net/mac80211/ht.c | 47 | ||||
-rw-r--r-- | net/mac80211/ieee80211_i.h | 14 | ||||
-rw-r--r-- | net/mac80211/main.c | 24 | ||||
-rw-r--r-- | net/mac80211/mlme.c | 63 | ||||
-rw-r--r-- | net/mac80211/status.c | 38 | ||||
-rw-r--r-- | net/mac80211/util.c | 74 |
12 files changed, 518 insertions, 12 deletions
diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 88e41176e7fd..92c669ebb358 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c | |||
@@ -618,12 +618,26 @@ static int mac80211_hwsim_config(struct ieee80211_hw *hw, u32 changed) | |||
618 | { | 618 | { |
619 | struct mac80211_hwsim_data *data = hw->priv; | 619 | struct mac80211_hwsim_data *data = hw->priv; |
620 | struct ieee80211_conf *conf = &hw->conf; | 620 | struct ieee80211_conf *conf = &hw->conf; |
621 | 621 | static const char *chantypes[4] = { | |
622 | printk(KERN_DEBUG "%s:%s (freq=%d idle=%d ps=%d)\n", | 622 | [NL80211_CHAN_NO_HT] = "noht", |
623 | [NL80211_CHAN_HT20] = "ht20", | ||
624 | [NL80211_CHAN_HT40MINUS] = "ht40-", | ||
625 | [NL80211_CHAN_HT40PLUS] = "ht40+", | ||
626 | }; | ||
627 | static const char *smps_modes[IEEE80211_SMPS_NUM_MODES] = { | ||
628 | [IEEE80211_SMPS_AUTOMATIC] = "auto", | ||
629 | [IEEE80211_SMPS_OFF] = "off", | ||
630 | [IEEE80211_SMPS_STATIC] = "static", | ||
631 | [IEEE80211_SMPS_DYNAMIC] = "dynamic", | ||
632 | }; | ||
633 | |||
634 | printk(KERN_DEBUG "%s:%s (freq=%d/%s idle=%d ps=%d smps=%s)\n", | ||
623 | wiphy_name(hw->wiphy), __func__, | 635 | wiphy_name(hw->wiphy), __func__, |
624 | conf->channel->center_freq, | 636 | conf->channel->center_freq, |
637 | chantypes[conf->channel_type], | ||
625 | !!(conf->flags & IEEE80211_CONF_IDLE), | 638 | !!(conf->flags & IEEE80211_CONF_IDLE), |
626 | !!(conf->flags & IEEE80211_CONF_PS)); | 639 | !!(conf->flags & IEEE80211_CONF_PS), |
640 | smps_modes[conf->smps_mode]); | ||
627 | 641 | ||
628 | data->idle = !!(conf->flags & IEEE80211_CONF_IDLE); | 642 | data->idle = !!(conf->flags & IEEE80211_CONF_IDLE); |
629 | 643 | ||
@@ -1082,7 +1096,9 @@ static int __init init_mac80211_hwsim(void) | |||
1082 | BIT(NL80211_IFTYPE_MESH_POINT); | 1096 | BIT(NL80211_IFTYPE_MESH_POINT); |
1083 | 1097 | ||
1084 | hw->flags = IEEE80211_HW_MFP_CAPABLE | | 1098 | hw->flags = IEEE80211_HW_MFP_CAPABLE | |
1085 | IEEE80211_HW_SIGNAL_DBM; | 1099 | IEEE80211_HW_SIGNAL_DBM | |
1100 | IEEE80211_HW_SUPPORTS_STATIC_SMPS | | ||
1101 | IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS; | ||
1086 | 1102 | ||
1087 | /* ask mac80211 to reserve space for magic */ | 1103 | /* ask mac80211 to reserve space for magic */ |
1088 | hw->vif_data_size = sizeof(struct hwsim_vif_priv); | 1104 | hw->vif_data_size = sizeof(struct hwsim_vif_priv); |
diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index d9724a28c0c2..e8d43d0ff2c3 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h | |||
@@ -707,6 +707,10 @@ struct ieee80211_mgmt { | |||
707 | u8 action; | 707 | u8 action; |
708 | u8 trans_id[WLAN_SA_QUERY_TR_ID_LEN]; | 708 | u8 trans_id[WLAN_SA_QUERY_TR_ID_LEN]; |
709 | } __attribute__ ((packed)) sa_query; | 709 | } __attribute__ ((packed)) sa_query; |
710 | struct { | ||
711 | u8 action; | ||
712 | u8 smps_control; | ||
713 | } __attribute__ ((packed)) ht_smps; | ||
710 | } u; | 714 | } u; |
711 | } __attribute__ ((packed)) action; | 715 | } __attribute__ ((packed)) action; |
712 | } u; | 716 | } u; |
@@ -824,6 +828,7 @@ struct ieee80211_ht_cap { | |||
824 | #define IEEE80211_HT_CAP_LDPC_CODING 0x0001 | 828 | #define IEEE80211_HT_CAP_LDPC_CODING 0x0001 |
825 | #define IEEE80211_HT_CAP_SUP_WIDTH_20_40 0x0002 | 829 | #define IEEE80211_HT_CAP_SUP_WIDTH_20_40 0x0002 |
826 | #define IEEE80211_HT_CAP_SM_PS 0x000C | 830 | #define IEEE80211_HT_CAP_SM_PS 0x000C |
831 | #define IEEE80211_HT_CAP_SM_PS_SHIFT 2 | ||
827 | #define IEEE80211_HT_CAP_GRN_FLD 0x0010 | 832 | #define IEEE80211_HT_CAP_GRN_FLD 0x0010 |
828 | #define IEEE80211_HT_CAP_SGI_20 0x0020 | 833 | #define IEEE80211_HT_CAP_SGI_20 0x0020 |
829 | #define IEEE80211_HT_CAP_SGI_40 0x0040 | 834 | #define IEEE80211_HT_CAP_SGI_40 0x0040 |
@@ -839,6 +844,7 @@ struct ieee80211_ht_cap { | |||
839 | /* 802.11n HT capability AMPDU settings (for ampdu_params_info) */ | 844 | /* 802.11n HT capability AMPDU settings (for ampdu_params_info) */ |
840 | #define IEEE80211_HT_AMPDU_PARM_FACTOR 0x03 | 845 | #define IEEE80211_HT_AMPDU_PARM_FACTOR 0x03 |
841 | #define IEEE80211_HT_AMPDU_PARM_DENSITY 0x1C | 846 | #define IEEE80211_HT_AMPDU_PARM_DENSITY 0x1C |
847 | #define IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT 2 | ||
842 | 848 | ||
843 | /* | 849 | /* |
844 | * Maximum length of AMPDU that the STA can receive. | 850 | * Maximum length of AMPDU that the STA can receive. |
@@ -922,12 +928,17 @@ struct ieee80211_ht_info { | |||
922 | #define IEEE80211_MAX_AMPDU_BUF 0x40 | 928 | #define IEEE80211_MAX_AMPDU_BUF 0x40 |
923 | 929 | ||
924 | 930 | ||
925 | /* Spatial Multiplexing Power Save Modes */ | 931 | /* Spatial Multiplexing Power Save Modes (for capability) */ |
926 | #define WLAN_HT_CAP_SM_PS_STATIC 0 | 932 | #define WLAN_HT_CAP_SM_PS_STATIC 0 |
927 | #define WLAN_HT_CAP_SM_PS_DYNAMIC 1 | 933 | #define WLAN_HT_CAP_SM_PS_DYNAMIC 1 |
928 | #define WLAN_HT_CAP_SM_PS_INVALID 2 | 934 | #define WLAN_HT_CAP_SM_PS_INVALID 2 |
929 | #define WLAN_HT_CAP_SM_PS_DISABLED 3 | 935 | #define WLAN_HT_CAP_SM_PS_DISABLED 3 |
930 | 936 | ||
937 | /* for SM power control field lower two bits */ | ||
938 | #define WLAN_HT_SMPS_CONTROL_DISABLED 0 | ||
939 | #define WLAN_HT_SMPS_CONTROL_STATIC 1 | ||
940 | #define WLAN_HT_SMPS_CONTROL_DYNAMIC 3 | ||
941 | |||
931 | /* Authentication algorithms */ | 942 | /* Authentication algorithms */ |
932 | #define WLAN_AUTH_OPEN 0 | 943 | #define WLAN_AUTH_OPEN 0 |
933 | #define WLAN_AUTH_SHARED_KEY 1 | 944 | #define WLAN_AUTH_SHARED_KEY 1 |
@@ -1150,6 +1161,18 @@ enum ieee80211_spectrum_mgmt_actioncode { | |||
1150 | WLAN_ACTION_SPCT_CHL_SWITCH = 4, | 1161 | WLAN_ACTION_SPCT_CHL_SWITCH = 4, |
1151 | }; | 1162 | }; |
1152 | 1163 | ||
1164 | /* HT action codes */ | ||
1165 | enum ieee80211_ht_actioncode { | ||
1166 | WLAN_HT_ACTION_NOTIFY_CHANWIDTH = 0, | ||
1167 | WLAN_HT_ACTION_SMPS = 1, | ||
1168 | WLAN_HT_ACTION_PSMP = 2, | ||
1169 | WLAN_HT_ACTION_PCO_PHASE = 3, | ||
1170 | WLAN_HT_ACTION_CSI = 4, | ||
1171 | WLAN_HT_ACTION_NONCOMPRESSED_BF = 5, | ||
1172 | WLAN_HT_ACTION_COMPRESSED_BF = 6, | ||
1173 | WLAN_HT_ACTION_ASEL_IDX_FEEDBACK = 7, | ||
1174 | }; | ||
1175 | |||
1153 | /* Security key length */ | 1176 | /* Security key length */ |
1154 | enum ieee80211_key_len { | 1177 | enum ieee80211_key_len { |
1155 | WLAN_KEY_LEN_WEP40 = 5, | 1178 | WLAN_KEY_LEN_WEP40 = 5, |
diff --git a/include/net/mac80211.h b/include/net/mac80211.h index e94cc526b0f6..e6b6bf81d5b9 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h | |||
@@ -597,8 +597,10 @@ enum ieee80211_conf_flags { | |||
597 | * @IEEE80211_CONF_CHANGE_CHANNEL: the channel/channel_type changed | 597 | * @IEEE80211_CONF_CHANGE_CHANNEL: the channel/channel_type changed |
598 | * @IEEE80211_CONF_CHANGE_RETRY_LIMITS: retry limits changed | 598 | * @IEEE80211_CONF_CHANGE_RETRY_LIMITS: retry limits changed |
599 | * @IEEE80211_CONF_CHANGE_IDLE: Idle flag changed | 599 | * @IEEE80211_CONF_CHANGE_IDLE: Idle flag changed |
600 | * @IEEE80211_CONF_CHANGE_SMPS: Spatial multiplexing powersave mode changed | ||
600 | */ | 601 | */ |
601 | enum ieee80211_conf_changed { | 602 | enum ieee80211_conf_changed { |
603 | IEEE80211_CONF_CHANGE_SMPS = BIT(1), | ||
602 | IEEE80211_CONF_CHANGE_LISTEN_INTERVAL = BIT(2), | 604 | IEEE80211_CONF_CHANGE_LISTEN_INTERVAL = BIT(2), |
603 | IEEE80211_CONF_CHANGE_MONITOR = BIT(3), | 605 | IEEE80211_CONF_CHANGE_MONITOR = BIT(3), |
604 | IEEE80211_CONF_CHANGE_PS = BIT(4), | 606 | IEEE80211_CONF_CHANGE_PS = BIT(4), |
@@ -609,6 +611,21 @@ enum ieee80211_conf_changed { | |||
609 | }; | 611 | }; |
610 | 612 | ||
611 | /** | 613 | /** |
614 | * enum ieee80211_smps_mode - spatial multiplexing power save mode | ||
615 | * | ||
616 | * @ | ||
617 | */ | ||
618 | enum ieee80211_smps_mode { | ||
619 | IEEE80211_SMPS_AUTOMATIC, | ||
620 | IEEE80211_SMPS_OFF, | ||
621 | IEEE80211_SMPS_STATIC, | ||
622 | IEEE80211_SMPS_DYNAMIC, | ||
623 | |||
624 | /* keep last */ | ||
625 | IEEE80211_SMPS_NUM_MODES, | ||
626 | }; | ||
627 | |||
628 | /** | ||
612 | * struct ieee80211_conf - configuration of the device | 629 | * struct ieee80211_conf - configuration of the device |
613 | * | 630 | * |
614 | * This struct indicates how the driver shall configure the hardware. | 631 | * This struct indicates how the driver shall configure the hardware. |
@@ -636,6 +653,10 @@ enum ieee80211_conf_changed { | |||
636 | * @short_frame_max_tx_count: Maximum number of transmissions for a "short" | 653 | * @short_frame_max_tx_count: Maximum number of transmissions for a "short" |
637 | * frame, called "dot11ShortRetryLimit" in 802.11, but actually means the | 654 | * frame, called "dot11ShortRetryLimit" in 802.11, but actually means the |
638 | * number of transmissions not the number of retries | 655 | * number of transmissions not the number of retries |
656 | * | ||
657 | * @smps_mode: spatial multiplexing powersave mode; note that | ||
658 | * %IEEE80211_SMPS_STATIC is used when the device is not | ||
659 | * configured for an HT channel | ||
639 | */ | 660 | */ |
640 | struct ieee80211_conf { | 661 | struct ieee80211_conf { |
641 | u32 flags; | 662 | u32 flags; |
@@ -648,6 +669,7 @@ struct ieee80211_conf { | |||
648 | 669 | ||
649 | struct ieee80211_channel *channel; | 670 | struct ieee80211_channel *channel; |
650 | enum nl80211_channel_type channel_type; | 671 | enum nl80211_channel_type channel_type; |
672 | enum ieee80211_smps_mode smps_mode; | ||
651 | }; | 673 | }; |
652 | 674 | ||
653 | /** | 675 | /** |
@@ -930,6 +952,16 @@ enum ieee80211_tkip_key_type { | |||
930 | * @IEEE80211_HW_BEACON_FILTER: | 952 | * @IEEE80211_HW_BEACON_FILTER: |
931 | * Hardware supports dropping of irrelevant beacon frames to | 953 | * Hardware supports dropping of irrelevant beacon frames to |
932 | * avoid waking up cpu. | 954 | * avoid waking up cpu. |
955 | * | ||
956 | * @IEEE80211_HW_SUPPORTS_STATIC_SMPS: | ||
957 | * Hardware supports static spatial multiplexing powersave, | ||
958 | * ie. can turn off all but one chain even on HT connections | ||
959 | * that should be using more chains. | ||
960 | * | ||
961 | * @IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS: | ||
962 | * Hardware supports dynamic spatial multiplexing powersave, | ||
963 | * ie. can turn off all but one chain and then wake the rest | ||
964 | * up as required after, for example, rts/cts handshake. | ||
933 | */ | 965 | */ |
934 | enum ieee80211_hw_flags { | 966 | enum ieee80211_hw_flags { |
935 | IEEE80211_HW_HAS_RATE_CONTROL = 1<<0, | 967 | IEEE80211_HW_HAS_RATE_CONTROL = 1<<0, |
@@ -947,6 +979,8 @@ enum ieee80211_hw_flags { | |||
947 | IEEE80211_HW_SUPPORTS_DYNAMIC_PS = 1<<12, | 979 | IEEE80211_HW_SUPPORTS_DYNAMIC_PS = 1<<12, |
948 | IEEE80211_HW_MFP_CAPABLE = 1<<13, | 980 | IEEE80211_HW_MFP_CAPABLE = 1<<13, |
949 | IEEE80211_HW_BEACON_FILTER = 1<<14, | 981 | IEEE80211_HW_BEACON_FILTER = 1<<14, |
982 | IEEE80211_HW_SUPPORTS_STATIC_SMPS = 1<<15, | ||
983 | IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS = 1<<16, | ||
950 | }; | 984 | }; |
951 | 985 | ||
952 | /** | 986 | /** |
@@ -1215,6 +1249,31 @@ ieee80211_get_alt_retry_rate(const struct ieee80211_hw *hw, | |||
1215 | */ | 1249 | */ |
1216 | 1250 | ||
1217 | /** | 1251 | /** |
1252 | * DOC: Spatial multiplexing power save | ||
1253 | * | ||
1254 | * SMPS (Spatial multiplexing power save) is a mechanism to conserve | ||
1255 | * power in an 802.11n implementation. For details on the mechanism | ||
1256 | * and rationale, please refer to 802.11 (as amended by 802.11n-2009) | ||
1257 | * "11.2.3 SM power save". | ||
1258 | * | ||
1259 | * The mac80211 implementation is capable of sending action frames | ||
1260 | * to update the AP about the station's SMPS mode, and will instruct | ||
1261 | * the driver to enter the specific mode. It will also announce the | ||
1262 | * requested SMPS mode during the association handshake. Hardware | ||
1263 | * support for this feature is required, and can be indicated by | ||
1264 | * hardware flags. | ||
1265 | * | ||
1266 | * The default mode will be "automatic", which nl80211/cfg80211 | ||
1267 | * defines to be dynamic SMPS in (regular) powersave, and SMPS | ||
1268 | * turned off otherwise. | ||
1269 | * | ||
1270 | * To support this feature, the driver must set the appropriate | ||
1271 | * hardware support flags, and handle the SMPS flag to the config() | ||
1272 | * operation. It will then with this mechanism be instructed to | ||
1273 | * enter the requested SMPS mode while associated to an HT AP. | ||
1274 | */ | ||
1275 | |||
1276 | /** | ||
1218 | * DOC: Frame filtering | 1277 | * DOC: Frame filtering |
1219 | * | 1278 | * |
1220 | * mac80211 requires to see many management frames for proper | 1279 | * mac80211 requires to see many management frames for proper |
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index fcfa1bf776a7..8c35418d1c96 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c | |||
@@ -1318,6 +1318,50 @@ static int ieee80211_testmode_cmd(struct wiphy *wiphy, void *data, int len) | |||
1318 | } | 1318 | } |
1319 | #endif | 1319 | #endif |
1320 | 1320 | ||
1321 | int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata, | ||
1322 | enum ieee80211_smps_mode smps_mode) | ||
1323 | { | ||
1324 | const u8 *ap; | ||
1325 | enum ieee80211_smps_mode old_req; | ||
1326 | int err; | ||
1327 | |||
1328 | old_req = sdata->u.mgd.req_smps; | ||
1329 | sdata->u.mgd.req_smps = smps_mode; | ||
1330 | |||
1331 | if (old_req == smps_mode && | ||
1332 | smps_mode != IEEE80211_SMPS_AUTOMATIC) | ||
1333 | return 0; | ||
1334 | |||
1335 | /* | ||
1336 | * If not associated, or current association is not an HT | ||
1337 | * association, there's no need to send an action frame. | ||
1338 | */ | ||
1339 | if (!sdata->u.mgd.associated || | ||
1340 | sdata->local->oper_channel_type == NL80211_CHAN_NO_HT) { | ||
1341 | mutex_lock(&sdata->local->iflist_mtx); | ||
1342 | ieee80211_recalc_smps(sdata->local, sdata); | ||
1343 | mutex_unlock(&sdata->local->iflist_mtx); | ||
1344 | return 0; | ||
1345 | } | ||
1346 | |||
1347 | ap = sdata->u.mgd.associated->cbss.bssid; | ||
1348 | |||
1349 | if (smps_mode == IEEE80211_SMPS_AUTOMATIC) { | ||
1350 | if (sdata->u.mgd.powersave) | ||
1351 | smps_mode = IEEE80211_SMPS_DYNAMIC; | ||
1352 | else | ||
1353 | smps_mode = IEEE80211_SMPS_OFF; | ||
1354 | } | ||
1355 | |||
1356 | /* send SM PS frame to AP */ | ||
1357 | err = ieee80211_send_smps_action(sdata, smps_mode, | ||
1358 | ap, ap); | ||
1359 | if (err) | ||
1360 | sdata->u.mgd.req_smps = old_req; | ||
1361 | |||
1362 | return err; | ||
1363 | } | ||
1364 | |||
1321 | static int ieee80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev, | 1365 | static int ieee80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev, |
1322 | bool enabled, int timeout) | 1366 | bool enabled, int timeout) |
1323 | { | 1367 | { |
@@ -1335,6 +1379,11 @@ static int ieee80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev, | |||
1335 | sdata->u.mgd.powersave = enabled; | 1379 | sdata->u.mgd.powersave = enabled; |
1336 | conf->dynamic_ps_timeout = timeout; | 1380 | conf->dynamic_ps_timeout = timeout; |
1337 | 1381 | ||
1382 | /* no change, but if automatic follow powersave */ | ||
1383 | mutex_lock(&sdata->u.mgd.mtx); | ||
1384 | __ieee80211_request_smps(sdata, sdata->u.mgd.req_smps); | ||
1385 | mutex_unlock(&sdata->u.mgd.mtx); | ||
1386 | |||
1338 | if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS) | 1387 | if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS) |
1339 | ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS); | 1388 | ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS); |
1340 | 1389 | ||
diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c index 5d9c797635a9..355983503885 100644 --- a/net/mac80211/debugfs_netdev.c +++ b/net/mac80211/debugfs_netdev.c | |||
@@ -41,6 +41,30 @@ static ssize_t ieee80211_if_read( | |||
41 | return ret; | 41 | return ret; |
42 | } | 42 | } |
43 | 43 | ||
44 | static ssize_t ieee80211_if_write( | ||
45 | struct ieee80211_sub_if_data *sdata, | ||
46 | const char __user *userbuf, | ||
47 | size_t count, loff_t *ppos, | ||
48 | ssize_t (*write)(struct ieee80211_sub_if_data *, const char *, int)) | ||
49 | { | ||
50 | u8 *buf; | ||
51 | ssize_t ret = -ENODEV; | ||
52 | |||
53 | buf = kzalloc(count, GFP_KERNEL); | ||
54 | if (!buf) | ||
55 | return -ENOMEM; | ||
56 | |||
57 | if (copy_from_user(buf, userbuf, count)) | ||
58 | return -EFAULT; | ||
59 | |||
60 | rtnl_lock(); | ||
61 | if (sdata->dev->reg_state == NETREG_REGISTERED) | ||
62 | ret = (*write)(sdata, buf, count); | ||
63 | rtnl_unlock(); | ||
64 | |||
65 | return ret; | ||
66 | } | ||
67 | |||
44 | #define IEEE80211_IF_FMT(name, field, format_string) \ | 68 | #define IEEE80211_IF_FMT(name, field, format_string) \ |
45 | static ssize_t ieee80211_if_fmt_##name( \ | 69 | static ssize_t ieee80211_if_fmt_##name( \ |
46 | const struct ieee80211_sub_if_data *sdata, char *buf, \ | 70 | const struct ieee80211_sub_if_data *sdata, char *buf, \ |
@@ -71,7 +95,7 @@ static ssize_t ieee80211_if_fmt_##name( \ | |||
71 | return scnprintf(buf, buflen, "%pM\n", sdata->field); \ | 95 | return scnprintf(buf, buflen, "%pM\n", sdata->field); \ |
72 | } | 96 | } |
73 | 97 | ||
74 | #define __IEEE80211_IF_FILE(name) \ | 98 | #define __IEEE80211_IF_FILE(name, _write) \ |
75 | static ssize_t ieee80211_if_read_##name(struct file *file, \ | 99 | static ssize_t ieee80211_if_read_##name(struct file *file, \ |
76 | char __user *userbuf, \ | 100 | char __user *userbuf, \ |
77 | size_t count, loff_t *ppos) \ | 101 | size_t count, loff_t *ppos) \ |
@@ -82,12 +106,24 @@ static ssize_t ieee80211_if_read_##name(struct file *file, \ | |||
82 | } \ | 106 | } \ |
83 | static const struct file_operations name##_ops = { \ | 107 | static const struct file_operations name##_ops = { \ |
84 | .read = ieee80211_if_read_##name, \ | 108 | .read = ieee80211_if_read_##name, \ |
109 | .write = (_write), \ | ||
85 | .open = mac80211_open_file_generic, \ | 110 | .open = mac80211_open_file_generic, \ |
86 | } | 111 | } |
87 | 112 | ||
113 | #define __IEEE80211_IF_FILE_W(name) \ | ||
114 | static ssize_t ieee80211_if_write_##name(struct file *file, \ | ||
115 | const char __user *userbuf, \ | ||
116 | size_t count, loff_t *ppos) \ | ||
117 | { \ | ||
118 | return ieee80211_if_write(file->private_data, userbuf, count, \ | ||
119 | ppos, ieee80211_if_parse_##name); \ | ||
120 | } \ | ||
121 | __IEEE80211_IF_FILE(name, ieee80211_if_write_##name) | ||
122 | |||
123 | |||
88 | #define IEEE80211_IF_FILE(name, field, format) \ | 124 | #define IEEE80211_IF_FILE(name, field, format) \ |
89 | IEEE80211_IF_FMT_##format(name, field) \ | 125 | IEEE80211_IF_FMT_##format(name, field) \ |
90 | __IEEE80211_IF_FILE(name) | 126 | __IEEE80211_IF_FILE(name, NULL) |
91 | 127 | ||
92 | /* common attributes */ | 128 | /* common attributes */ |
93 | IEEE80211_IF_FILE(drop_unencrypted, drop_unencrypted, DEC); | 129 | IEEE80211_IF_FILE(drop_unencrypted, drop_unencrypted, DEC); |
@@ -99,6 +135,70 @@ IEEE80211_IF_FILE(bssid, u.mgd.bssid, MAC); | |||
99 | IEEE80211_IF_FILE(aid, u.mgd.aid, DEC); | 135 | IEEE80211_IF_FILE(aid, u.mgd.aid, DEC); |
100 | IEEE80211_IF_FILE(capab, u.mgd.capab, HEX); | 136 | IEEE80211_IF_FILE(capab, u.mgd.capab, HEX); |
101 | 137 | ||
138 | static int ieee80211_set_smps(struct ieee80211_sub_if_data *sdata, | ||
139 | enum ieee80211_smps_mode smps_mode) | ||
140 | { | ||
141 | struct ieee80211_local *local = sdata->local; | ||
142 | int err; | ||
143 | |||
144 | if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_STATIC_SMPS) && | ||
145 | smps_mode == IEEE80211_SMPS_STATIC) | ||
146 | return -EINVAL; | ||
147 | |||
148 | /* auto should be dynamic if in PS mode */ | ||
149 | if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS) && | ||
150 | (smps_mode == IEEE80211_SMPS_DYNAMIC || | ||
151 | smps_mode == IEEE80211_SMPS_AUTOMATIC)) | ||
152 | return -EINVAL; | ||
153 | |||
154 | /* supported only on managed interfaces for now */ | ||
155 | if (sdata->vif.type != NL80211_IFTYPE_STATION) | ||
156 | return -EOPNOTSUPP; | ||
157 | |||
158 | mutex_lock(&local->iflist_mtx); | ||
159 | err = __ieee80211_request_smps(sdata, smps_mode); | ||
160 | mutex_unlock(&local->iflist_mtx); | ||
161 | |||
162 | return err; | ||
163 | } | ||
164 | |||
165 | static const char *smps_modes[IEEE80211_SMPS_NUM_MODES] = { | ||
166 | [IEEE80211_SMPS_AUTOMATIC] = "auto", | ||
167 | [IEEE80211_SMPS_OFF] = "off", | ||
168 | [IEEE80211_SMPS_STATIC] = "static", | ||
169 | [IEEE80211_SMPS_DYNAMIC] = "dynamic", | ||
170 | }; | ||
171 | |||
172 | static ssize_t ieee80211_if_fmt_smps(const struct ieee80211_sub_if_data *sdata, | ||
173 | char *buf, int buflen) | ||
174 | { | ||
175 | if (sdata->vif.type != NL80211_IFTYPE_STATION) | ||
176 | return -EOPNOTSUPP; | ||
177 | |||
178 | return snprintf(buf, buflen, "request: %s\nused: %s\n", | ||
179 | smps_modes[sdata->u.mgd.req_smps], | ||
180 | smps_modes[sdata->u.mgd.ap_smps]); | ||
181 | } | ||
182 | |||
183 | static ssize_t ieee80211_if_parse_smps(struct ieee80211_sub_if_data *sdata, | ||
184 | const char *buf, int buflen) | ||
185 | { | ||
186 | enum ieee80211_smps_mode mode; | ||
187 | |||
188 | for (mode = 0; mode < IEEE80211_SMPS_NUM_MODES; mode++) { | ||
189 | if (strncmp(buf, smps_modes[mode], buflen) == 0) { | ||
190 | int err = ieee80211_set_smps(sdata, mode); | ||
191 | if (!err) | ||
192 | return buflen; | ||
193 | return err; | ||
194 | } | ||
195 | } | ||
196 | |||
197 | return -EINVAL; | ||
198 | } | ||
199 | |||
200 | __IEEE80211_IF_FILE_W(smps); | ||
201 | |||
102 | /* AP attributes */ | 202 | /* AP attributes */ |
103 | IEEE80211_IF_FILE(num_sta_ps, u.ap.num_sta_ps, ATOMIC); | 203 | IEEE80211_IF_FILE(num_sta_ps, u.ap.num_sta_ps, ATOMIC); |
104 | IEEE80211_IF_FILE(dtim_count, u.ap.dtim_count, DEC); | 204 | IEEE80211_IF_FILE(dtim_count, u.ap.dtim_count, DEC); |
@@ -109,7 +209,7 @@ static ssize_t ieee80211_if_fmt_num_buffered_multicast( | |||
109 | return scnprintf(buf, buflen, "%u\n", | 209 | return scnprintf(buf, buflen, "%u\n", |
110 | skb_queue_len(&sdata->u.ap.ps_bc_buf)); | 210 | skb_queue_len(&sdata->u.ap.ps_bc_buf)); |
111 | } | 211 | } |
112 | __IEEE80211_IF_FILE(num_buffered_multicast); | 212 | __IEEE80211_IF_FILE(num_buffered_multicast, NULL); |
113 | 213 | ||
114 | /* WDS attributes */ | 214 | /* WDS attributes */ |
115 | IEEE80211_IF_FILE(peer, u.wds.remote_addr, MAC); | 215 | IEEE80211_IF_FILE(peer, u.wds.remote_addr, MAC); |
@@ -158,6 +258,10 @@ IEEE80211_IF_FILE(dot11MeshHWMPRootMode, | |||
158 | debugfs_create_file(#name, 0400, sdata->debugfs.dir, \ | 258 | debugfs_create_file(#name, 0400, sdata->debugfs.dir, \ |
159 | sdata, &name##_ops); | 259 | sdata, &name##_ops); |
160 | 260 | ||
261 | #define DEBUGFS_ADD_MODE(name, mode) \ | ||
262 | debugfs_create_file(#name, mode, sdata->debugfs.dir, \ | ||
263 | sdata, &name##_ops); | ||
264 | |||
161 | static void add_sta_files(struct ieee80211_sub_if_data *sdata) | 265 | static void add_sta_files(struct ieee80211_sub_if_data *sdata) |
162 | { | 266 | { |
163 | DEBUGFS_ADD(drop_unencrypted, sta); | 267 | DEBUGFS_ADD(drop_unencrypted, sta); |
@@ -167,6 +271,7 @@ static void add_sta_files(struct ieee80211_sub_if_data *sdata) | |||
167 | DEBUGFS_ADD(bssid, sta); | 271 | DEBUGFS_ADD(bssid, sta); |
168 | DEBUGFS_ADD(aid, sta); | 272 | DEBUGFS_ADD(aid, sta); |
169 | DEBUGFS_ADD(capab, sta); | 273 | DEBUGFS_ADD(capab, sta); |
274 | DEBUGFS_ADD_MODE(smps, 0600); | ||
170 | } | 275 | } |
171 | 276 | ||
172 | static void add_ap_files(struct ieee80211_sub_if_data *sdata) | 277 | static void add_ap_files(struct ieee80211_sub_if_data *sdata) |
diff --git a/net/mac80211/driver-trace.h b/net/mac80211/driver-trace.h index ee2d19a25ce1..7a849b920165 100644 --- a/net/mac80211/driver-trace.h +++ b/net/mac80211/driver-trace.h | |||
@@ -140,6 +140,7 @@ TRACE_EVENT(drv_config, | |||
140 | __field(u8, short_frame_max_tx_count) | 140 | __field(u8, short_frame_max_tx_count) |
141 | __field(int, center_freq) | 141 | __field(int, center_freq) |
142 | __field(int, channel_type) | 142 | __field(int, channel_type) |
143 | __field(int, smps) | ||
143 | ), | 144 | ), |
144 | 145 | ||
145 | TP_fast_assign( | 146 | TP_fast_assign( |
@@ -155,6 +156,7 @@ TRACE_EVENT(drv_config, | |||
155 | __entry->short_frame_max_tx_count = local->hw.conf.short_frame_max_tx_count; | 156 | __entry->short_frame_max_tx_count = local->hw.conf.short_frame_max_tx_count; |
156 | __entry->center_freq = local->hw.conf.channel->center_freq; | 157 | __entry->center_freq = local->hw.conf.channel->center_freq; |
157 | __entry->channel_type = local->hw.conf.channel_type; | 158 | __entry->channel_type = local->hw.conf.channel_type; |
159 | __entry->smps = local->hw.conf.smps_mode; | ||
158 | ), | 160 | ), |
159 | 161 | ||
160 | TP_printk( | 162 | TP_printk( |
diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c index 45ebd062a2fb..63b8f86b7f16 100644 --- a/net/mac80211/ht.c +++ b/net/mac80211/ht.c | |||
@@ -166,3 +166,50 @@ void ieee80211_process_delba(struct ieee80211_sub_if_data *sdata, | |||
166 | spin_unlock_bh(&sta->lock); | 166 | spin_unlock_bh(&sta->lock); |
167 | } | 167 | } |
168 | } | 168 | } |
169 | |||
170 | int ieee80211_send_smps_action(struct ieee80211_sub_if_data *sdata, | ||
171 | enum ieee80211_smps_mode smps, const u8 *da, | ||
172 | const u8 *bssid) | ||
173 | { | ||
174 | struct ieee80211_local *local = sdata->local; | ||
175 | struct sk_buff *skb; | ||
176 | struct ieee80211_mgmt *action_frame; | ||
177 | |||
178 | /* 27 = header + category + action + smps mode */ | ||
179 | skb = dev_alloc_skb(27 + local->hw.extra_tx_headroom); | ||
180 | if (!skb) | ||
181 | return -ENOMEM; | ||
182 | |||
183 | skb_reserve(skb, local->hw.extra_tx_headroom); | ||
184 | action_frame = (void *)skb_put(skb, 27); | ||
185 | memcpy(action_frame->da, da, ETH_ALEN); | ||
186 | memcpy(action_frame->sa, sdata->dev->dev_addr, ETH_ALEN); | ||
187 | memcpy(action_frame->bssid, bssid, ETH_ALEN); | ||
188 | action_frame->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | | ||
189 | IEEE80211_STYPE_ACTION); | ||
190 | action_frame->u.action.category = WLAN_CATEGORY_HT; | ||
191 | action_frame->u.action.u.ht_smps.action = WLAN_HT_ACTION_SMPS; | ||
192 | switch (smps) { | ||
193 | case IEEE80211_SMPS_AUTOMATIC: | ||
194 | case IEEE80211_SMPS_NUM_MODES: | ||
195 | WARN_ON(1); | ||
196 | case IEEE80211_SMPS_OFF: | ||
197 | action_frame->u.action.u.ht_smps.smps_control = | ||
198 | WLAN_HT_SMPS_CONTROL_DISABLED; | ||
199 | break; | ||
200 | case IEEE80211_SMPS_STATIC: | ||
201 | action_frame->u.action.u.ht_smps.smps_control = | ||
202 | WLAN_HT_SMPS_CONTROL_STATIC; | ||
203 | break; | ||
204 | case IEEE80211_SMPS_DYNAMIC: | ||
205 | action_frame->u.action.u.ht_smps.smps_control = | ||
206 | WLAN_HT_SMPS_CONTROL_DYNAMIC; | ||
207 | break; | ||
208 | } | ||
209 | |||
210 | /* we'll do more on status of this frame */ | ||
211 | IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS; | ||
212 | ieee80211_tx_skb(sdata, skb); | ||
213 | |||
214 | return 0; | ||
215 | } | ||
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 178e329f9257..e63aecbddfbe 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h | |||
@@ -297,6 +297,8 @@ struct ieee80211_if_managed { | |||
297 | 297 | ||
298 | unsigned long timers_running; /* used for quiesce/restart */ | 298 | unsigned long timers_running; /* used for quiesce/restart */ |
299 | bool powersave; /* powersave requested for this iface */ | 299 | bool powersave; /* powersave requested for this iface */ |
300 | enum ieee80211_smps_mode req_smps, /* requested smps mode */ | ||
301 | ap_smps; /* smps mode AP thinks we're in */ | ||
300 | 302 | ||
301 | unsigned long request; | 303 | unsigned long request; |
302 | 304 | ||
@@ -587,6 +589,9 @@ struct ieee80211_local { | |||
587 | /* used for uploading changed mc list */ | 589 | /* used for uploading changed mc list */ |
588 | struct work_struct reconfig_filter; | 590 | struct work_struct reconfig_filter; |
589 | 591 | ||
592 | /* used to reconfigure hardware SM PS */ | ||
593 | struct work_struct recalc_smps; | ||
594 | |||
590 | /* aggregated multicast list */ | 595 | /* aggregated multicast list */ |
591 | struct dev_addr_list *mc_list; | 596 | struct dev_addr_list *mc_list; |
592 | int mc_count; | 597 | int mc_count; |
@@ -760,6 +765,8 @@ struct ieee80211_local { | |||
760 | int user_power_level; /* in dBm */ | 765 | int user_power_level; /* in dBm */ |
761 | int power_constr_level; /* in dBm */ | 766 | int power_constr_level; /* in dBm */ |
762 | 767 | ||
768 | enum ieee80211_smps_mode smps_mode; | ||
769 | |||
763 | struct work_struct restart_work; | 770 | struct work_struct restart_work; |
764 | 771 | ||
765 | #ifdef CONFIG_MAC80211_DEBUGFS | 772 | #ifdef CONFIG_MAC80211_DEBUGFS |
@@ -978,6 +985,9 @@ void ieee80211_send_bar(struct ieee80211_sub_if_data *sdata, u8 *ra, u16 tid, u1 | |||
978 | void ieee80211_send_delba(struct ieee80211_sub_if_data *sdata, | 985 | void ieee80211_send_delba(struct ieee80211_sub_if_data *sdata, |
979 | const u8 *da, u16 tid, | 986 | const u8 *da, u16 tid, |
980 | u16 initiator, u16 reason_code); | 987 | u16 initiator, u16 reason_code); |
988 | int ieee80211_send_smps_action(struct ieee80211_sub_if_data *sdata, | ||
989 | enum ieee80211_smps_mode smps, const u8 *da, | ||
990 | const u8 *bssid); | ||
981 | 991 | ||
982 | void ieee80211_sta_stop_rx_ba_session(struct ieee80211_sub_if_data *sdata, u8 *da, | 992 | void ieee80211_sta_stop_rx_ba_session(struct ieee80211_sub_if_data *sdata, u8 *da, |
983 | u16 tid, u16 initiator, u16 reason); | 993 | u16 tid, u16 initiator, u16 reason); |
@@ -1088,6 +1098,10 @@ void ieee80211_sta_def_wmm_params(struct ieee80211_sub_if_data *sdata, | |||
1088 | u32 ieee80211_sta_get_rates(struct ieee80211_local *local, | 1098 | u32 ieee80211_sta_get_rates(struct ieee80211_local *local, |
1089 | struct ieee802_11_elems *elems, | 1099 | struct ieee802_11_elems *elems, |
1090 | enum ieee80211_band band); | 1100 | enum ieee80211_band band); |
1101 | int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata, | ||
1102 | enum ieee80211_smps_mode smps_mode); | ||
1103 | void ieee80211_recalc_smps(struct ieee80211_local *local, | ||
1104 | struct ieee80211_sub_if_data *forsdata); | ||
1091 | 1105 | ||
1092 | #ifdef CONFIG_MAC80211_NOINLINE | 1106 | #ifdef CONFIG_MAC80211_NOINLINE |
1093 | #define debug_noinline noinline | 1107 | #define debug_noinline noinline |
diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 98320a94c270..e1293e8ed83a 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c | |||
@@ -113,6 +113,18 @@ int ieee80211_hw_config(struct ieee80211_local *local, u32 changed) | |||
113 | changed |= IEEE80211_CONF_CHANGE_CHANNEL; | 113 | changed |= IEEE80211_CONF_CHANGE_CHANNEL; |
114 | } | 114 | } |
115 | 115 | ||
116 | if (!conf_is_ht(&local->hw.conf)) { | ||
117 | /* | ||
118 | * mac80211.h documents that this is only valid | ||
119 | * when the channel is set to an HT type, and | ||
120 | * that otherwise STATIC is used. | ||
121 | */ | ||
122 | local->hw.conf.smps_mode = IEEE80211_SMPS_STATIC; | ||
123 | } else if (local->hw.conf.smps_mode != local->smps_mode) { | ||
124 | local->hw.conf.smps_mode = local->smps_mode; | ||
125 | changed |= IEEE80211_CONF_CHANGE_SMPS; | ||
126 | } | ||
127 | |||
116 | if (scan_chan) | 128 | if (scan_chan) |
117 | power = chan->max_power; | 129 | power = chan->max_power; |
118 | else | 130 | else |
@@ -297,6 +309,16 @@ void ieee80211_restart_hw(struct ieee80211_hw *hw) | |||
297 | } | 309 | } |
298 | EXPORT_SYMBOL(ieee80211_restart_hw); | 310 | EXPORT_SYMBOL(ieee80211_restart_hw); |
299 | 311 | ||
312 | static void ieee80211_recalc_smps_work(struct work_struct *work) | ||
313 | { | ||
314 | struct ieee80211_local *local = | ||
315 | container_of(work, struct ieee80211_local, recalc_smps); | ||
316 | |||
317 | mutex_lock(&local->iflist_mtx); | ||
318 | ieee80211_recalc_smps(local, NULL); | ||
319 | mutex_unlock(&local->iflist_mtx); | ||
320 | } | ||
321 | |||
300 | struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, | 322 | struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, |
301 | const struct ieee80211_ops *ops) | 323 | const struct ieee80211_ops *ops) |
302 | { | 324 | { |
@@ -370,6 +392,8 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, | |||
370 | INIT_WORK(&local->restart_work, ieee80211_restart_work); | 392 | INIT_WORK(&local->restart_work, ieee80211_restart_work); |
371 | 393 | ||
372 | INIT_WORK(&local->reconfig_filter, ieee80211_reconfig_filter); | 394 | INIT_WORK(&local->reconfig_filter, ieee80211_reconfig_filter); |
395 | INIT_WORK(&local->recalc_smps, ieee80211_recalc_smps_work); | ||
396 | local->smps_mode = IEEE80211_SMPS_OFF; | ||
373 | 397 | ||
374 | INIT_WORK(&local->dynamic_ps_enable_work, | 398 | INIT_WORK(&local->dynamic_ps_enable_work, |
375 | ieee80211_dynamic_ps_enable_work); | 399 | ieee80211_dynamic_ps_enable_work); |
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index cd5dcc3d8c2b..0a762a9ba4df 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c | |||
@@ -398,6 +398,8 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata, | |||
398 | __le16 tmp; | 398 | __le16 tmp; |
399 | u32 flags = local->hw.conf.channel->flags; | 399 | u32 flags = local->hw.conf.channel->flags; |
400 | 400 | ||
401 | /* determine capability flags */ | ||
402 | |||
401 | switch (ht_info->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) { | 403 | switch (ht_info->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) { |
402 | case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: | 404 | case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: |
403 | if (flags & IEEE80211_CHAN_NO_HT40PLUS) { | 405 | if (flags & IEEE80211_CHAN_NO_HT40PLUS) { |
@@ -413,17 +415,64 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata, | |||
413 | break; | 415 | break; |
414 | } | 416 | } |
415 | 417 | ||
416 | tmp = cpu_to_le16(cap); | 418 | /* set SM PS mode properly */ |
417 | pos = skb_put(skb, sizeof(struct ieee80211_ht_cap)+2); | 419 | cap &= ~IEEE80211_HT_CAP_SM_PS; |
420 | /* new association always uses requested smps mode */ | ||
421 | if (ifmgd->req_smps == IEEE80211_SMPS_AUTOMATIC) { | ||
422 | if (ifmgd->powersave) | ||
423 | ifmgd->ap_smps = IEEE80211_SMPS_DYNAMIC; | ||
424 | else | ||
425 | ifmgd->ap_smps = IEEE80211_SMPS_OFF; | ||
426 | } else | ||
427 | ifmgd->ap_smps = ifmgd->req_smps; | ||
428 | |||
429 | switch (ifmgd->ap_smps) { | ||
430 | case IEEE80211_SMPS_AUTOMATIC: | ||
431 | case IEEE80211_SMPS_NUM_MODES: | ||
432 | WARN_ON(1); | ||
433 | case IEEE80211_SMPS_OFF: | ||
434 | cap |= WLAN_HT_CAP_SM_PS_DISABLED << | ||
435 | IEEE80211_HT_CAP_SM_PS_SHIFT; | ||
436 | break; | ||
437 | case IEEE80211_SMPS_STATIC: | ||
438 | cap |= WLAN_HT_CAP_SM_PS_STATIC << | ||
439 | IEEE80211_HT_CAP_SM_PS_SHIFT; | ||
440 | break; | ||
441 | case IEEE80211_SMPS_DYNAMIC: | ||
442 | cap |= WLAN_HT_CAP_SM_PS_DYNAMIC << | ||
443 | IEEE80211_HT_CAP_SM_PS_SHIFT; | ||
444 | break; | ||
445 | } | ||
446 | |||
447 | /* reserve and fill IE */ | ||
448 | |||
449 | pos = skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2); | ||
418 | *pos++ = WLAN_EID_HT_CAPABILITY; | 450 | *pos++ = WLAN_EID_HT_CAPABILITY; |
419 | *pos++ = sizeof(struct ieee80211_ht_cap); | 451 | *pos++ = sizeof(struct ieee80211_ht_cap); |
420 | memset(pos, 0, sizeof(struct ieee80211_ht_cap)); | 452 | memset(pos, 0, sizeof(struct ieee80211_ht_cap)); |
453 | |||
454 | /* capability flags */ | ||
455 | tmp = cpu_to_le16(cap); | ||
421 | memcpy(pos, &tmp, sizeof(u16)); | 456 | memcpy(pos, &tmp, sizeof(u16)); |
422 | pos += sizeof(u16); | 457 | pos += sizeof(u16); |
423 | /* TODO: needs a define here for << 2 */ | 458 | |
459 | /* AMPDU parameters */ | ||
424 | *pos++ = sband->ht_cap.ampdu_factor | | 460 | *pos++ = sband->ht_cap.ampdu_factor | |
425 | (sband->ht_cap.ampdu_density << 2); | 461 | (sband->ht_cap.ampdu_density << |
462 | IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT); | ||
463 | |||
464 | /* MCS set */ | ||
426 | memcpy(pos, &sband->ht_cap.mcs, sizeof(sband->ht_cap.mcs)); | 465 | memcpy(pos, &sband->ht_cap.mcs, sizeof(sband->ht_cap.mcs)); |
466 | pos += sizeof(sband->ht_cap.mcs); | ||
467 | |||
468 | /* extended capabilities */ | ||
469 | pos += sizeof(__le16); | ||
470 | |||
471 | /* BF capabilities */ | ||
472 | pos += sizeof(__le32); | ||
473 | |||
474 | /* antenna selection */ | ||
475 | pos += sizeof(u8); | ||
427 | } | 476 | } |
428 | 477 | ||
429 | IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; | 478 | IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; |
@@ -932,6 +981,7 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, | |||
932 | 981 | ||
933 | mutex_lock(&local->iflist_mtx); | 982 | mutex_lock(&local->iflist_mtx); |
934 | ieee80211_recalc_ps(local, -1); | 983 | ieee80211_recalc_ps(local, -1); |
984 | ieee80211_recalc_smps(local, sdata); | ||
935 | mutex_unlock(&local->iflist_mtx); | 985 | mutex_unlock(&local->iflist_mtx); |
936 | 986 | ||
937 | netif_start_queue(sdata->dev); | 987 | netif_start_queue(sdata->dev); |
@@ -2327,6 +2377,11 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata) | |||
2327 | ifmgd->flags |= IEEE80211_STA_WMM_ENABLED; | 2377 | ifmgd->flags |= IEEE80211_STA_WMM_ENABLED; |
2328 | 2378 | ||
2329 | mutex_init(&ifmgd->mtx); | 2379 | mutex_init(&ifmgd->mtx); |
2380 | |||
2381 | if (sdata->local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS) | ||
2382 | ifmgd->req_smps = IEEE80211_SMPS_AUTOMATIC; | ||
2383 | else | ||
2384 | ifmgd->req_smps = IEEE80211_SMPS_OFF; | ||
2330 | } | 2385 | } |
2331 | 2386 | ||
2332 | /* scan finished notification */ | 2387 | /* scan finished notification */ |
diff --git a/net/mac80211/status.c b/net/mac80211/status.c index b4608f11a40f..0c0850d37dda 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c | |||
@@ -134,6 +134,40 @@ static void ieee80211_handle_filtered_frame(struct ieee80211_local *local, | |||
134 | dev_kfree_skb(skb); | 134 | dev_kfree_skb(skb); |
135 | } | 135 | } |
136 | 136 | ||
137 | static void ieee80211_frame_acked(struct sta_info *sta, struct sk_buff *skb) | ||
138 | { | ||
139 | struct ieee80211_mgmt *mgmt = (void *) skb->data; | ||
140 | struct ieee80211_local *local = sta->local; | ||
141 | struct ieee80211_sub_if_data *sdata = sta->sdata; | ||
142 | |||
143 | if (ieee80211_is_action(mgmt->frame_control) && | ||
144 | sdata->vif.type == NL80211_IFTYPE_STATION && | ||
145 | mgmt->u.action.category == WLAN_CATEGORY_HT && | ||
146 | mgmt->u.action.u.ht_smps.action == WLAN_HT_ACTION_SMPS) { | ||
147 | /* | ||
148 | * This update looks racy, but isn't -- if we come | ||
149 | * here we've definitely got a station that we're | ||
150 | * talking to, and on a managed interface that can | ||
151 | * only be the AP. And the only other place updating | ||
152 | * this variable is before we're associated. | ||
153 | */ | ||
154 | switch (mgmt->u.action.u.ht_smps.smps_control) { | ||
155 | case WLAN_HT_SMPS_CONTROL_DYNAMIC: | ||
156 | sta->sdata->u.mgd.ap_smps = IEEE80211_SMPS_DYNAMIC; | ||
157 | break; | ||
158 | case WLAN_HT_SMPS_CONTROL_STATIC: | ||
159 | sta->sdata->u.mgd.ap_smps = IEEE80211_SMPS_STATIC; | ||
160 | break; | ||
161 | case WLAN_HT_SMPS_CONTROL_DISABLED: | ||
162 | default: /* shouldn't happen since we don't send that */ | ||
163 | sta->sdata->u.mgd.ap_smps = IEEE80211_SMPS_OFF; | ||
164 | break; | ||
165 | } | ||
166 | |||
167 | ieee80211_queue_work(&local->hw, &local->recalc_smps); | ||
168 | } | ||
169 | } | ||
170 | |||
137 | void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) | 171 | void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) |
138 | { | 172 | { |
139 | struct sk_buff *skb2; | 173 | struct sk_buff *skb2; |
@@ -210,6 +244,10 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) | |||
210 | rate_control_tx_status(local, sband, sta, skb); | 244 | rate_control_tx_status(local, sband, sta, skb); |
211 | if (ieee80211_vif_is_mesh(&sta->sdata->vif)) | 245 | if (ieee80211_vif_is_mesh(&sta->sdata->vif)) |
212 | ieee80211s_update_metric(local, sta, skb); | 246 | ieee80211s_update_metric(local, sta, skb); |
247 | |||
248 | if (!(info->flags & IEEE80211_TX_CTL_INJECTED) && | ||
249 | (info->flags & IEEE80211_TX_STAT_ACK)) | ||
250 | ieee80211_frame_acked(sta, skb); | ||
213 | } | 251 | } |
214 | 252 | ||
215 | rcu_read_unlock(); | 253 | rcu_read_unlock(); |
diff --git a/net/mac80211/util.c b/net/mac80211/util.c index d54dbe8e09ba..086ef6257b4b 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c | |||
@@ -1170,3 +1170,77 @@ int ieee80211_reconfig(struct ieee80211_local *local) | |||
1170 | return 0; | 1170 | return 0; |
1171 | } | 1171 | } |
1172 | 1172 | ||
1173 | static int check_mgd_smps(struct ieee80211_if_managed *ifmgd, | ||
1174 | enum ieee80211_smps_mode *smps_mode) | ||
1175 | { | ||
1176 | if (ifmgd->associated) { | ||
1177 | *smps_mode = ifmgd->ap_smps; | ||
1178 | |||
1179 | if (*smps_mode == IEEE80211_SMPS_AUTOMATIC) { | ||
1180 | if (ifmgd->powersave) | ||
1181 | *smps_mode = IEEE80211_SMPS_DYNAMIC; | ||
1182 | else | ||
1183 | *smps_mode = IEEE80211_SMPS_OFF; | ||
1184 | } | ||
1185 | |||
1186 | return 1; | ||
1187 | } | ||
1188 | |||
1189 | return 0; | ||
1190 | } | ||
1191 | |||
1192 | /* must hold iflist_mtx */ | ||
1193 | void ieee80211_recalc_smps(struct ieee80211_local *local, | ||
1194 | struct ieee80211_sub_if_data *forsdata) | ||
1195 | { | ||
1196 | struct ieee80211_sub_if_data *sdata; | ||
1197 | enum ieee80211_smps_mode smps_mode = IEEE80211_SMPS_OFF; | ||
1198 | int count = 0; | ||
1199 | |||
1200 | if (forsdata) | ||
1201 | WARN_ON(!mutex_is_locked(&forsdata->u.mgd.mtx)); | ||
1202 | |||
1203 | WARN_ON(!mutex_is_locked(&local->iflist_mtx)); | ||
1204 | |||
1205 | /* | ||
1206 | * This function could be improved to handle multiple | ||
1207 | * interfaces better, but right now it makes any | ||
1208 | * non-station interfaces force SM PS to be turned | ||
1209 | * off. If there are multiple station interfaces it | ||
1210 | * could also use the best possible mode, e.g. if | ||
1211 | * one is in static and the other in dynamic then | ||
1212 | * dynamic is ok. | ||
1213 | */ | ||
1214 | |||
1215 | list_for_each_entry(sdata, &local->interfaces, list) { | ||
1216 | if (!netif_running(sdata->dev)) | ||
1217 | continue; | ||
1218 | if (sdata->vif.type != NL80211_IFTYPE_STATION) | ||
1219 | goto set; | ||
1220 | if (sdata != forsdata) { | ||
1221 | /* | ||
1222 | * This nested is ok -- we are holding the iflist_mtx | ||
1223 | * so can't get here twice or so. But it's required | ||
1224 | * since normally we acquire it first and then the | ||
1225 | * iflist_mtx. | ||
1226 | */ | ||
1227 | mutex_lock_nested(&sdata->u.mgd.mtx, SINGLE_DEPTH_NESTING); | ||
1228 | count += check_mgd_smps(&sdata->u.mgd, &smps_mode); | ||
1229 | mutex_unlock(&sdata->u.mgd.mtx); | ||
1230 | } else | ||
1231 | count += check_mgd_smps(&sdata->u.mgd, &smps_mode); | ||
1232 | |||
1233 | if (count > 1) { | ||
1234 | smps_mode = IEEE80211_SMPS_OFF; | ||
1235 | break; | ||
1236 | } | ||
1237 | } | ||
1238 | |||
1239 | if (smps_mode == local->smps_mode) | ||
1240 | return; | ||
1241 | |||
1242 | set: | ||
1243 | local->smps_mode = smps_mode; | ||
1244 | /* changed flag is auto-detected for this */ | ||
1245 | ieee80211_hw_config(local, 0); | ||
1246 | } | ||