* [PATCH 0/2] Spatial multiplexing powersave (client support)
@ 2009-11-30 15:38 Johannes Berg
2009-11-30 15:38 ` [PATCH 1/2] cfg80211: add SM PS hooks Johannes Berg
` (2 more replies)
0 siblings, 3 replies; 12+ messages in thread
From: Johannes Berg @ 2009-11-30 15:38 UTC (permalink / raw)
To: John Linville; +Cc: linux-wireless
These two patches implement spatial multiplexing power
save along with sending action frames if userspace
requests changes while associated.
The default is being in dynamic SM PS when powersave is
requested, and SM PS turned off when not in powersave.
Right now, only station interfaces are supported.
johannes
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH 1/2] cfg80211: add SM PS hooks
2009-11-30 15:38 [PATCH 0/2] Spatial multiplexing powersave (client support) Johannes Berg
@ 2009-11-30 15:38 ` Johannes Berg
2009-12-01 7:01 ` Kalle Valo
2009-11-30 15:38 ` [PATCH 2/2] mac80211: enable spatial multiplexing powersave Johannes Berg
2009-12-01 12:37 ` [PATCH v3] " Johannes Berg
2 siblings, 1 reply; 12+ messages in thread
From: Johannes Berg @ 2009-11-30 15:38 UTC (permalink / raw)
To: John Linville; +Cc: linux-wireless
Add spatial multiplexing power save configuration
hooks to cfg80211/nl80211.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
---
include/linux/nl80211.h | 32 ++++++++++++++++++++++++++++++++
include/net/cfg80211.h | 5 ++++-
net/wireless/nl80211.c | 21 +++++++++++++++++++++
3 files changed, 57 insertions(+), 1 deletion(-)
--- wireless-testing.orig/include/linux/nl80211.h 2009-11-29 11:33:01.000000000 +0100
+++ wireless-testing/include/linux/nl80211.h 2009-11-29 11:34:11.000000000 +0100
@@ -606,6 +606,11 @@ enum nl80211_commands {
* @NL80211_ATTR_MAX_NUM_PMKIDS: maximum number of PMKIDs a firmware can
* cache, a wiphy attribute.
*
+ * @NL80211_ATTR_SMPS_MODE: Used with change interface, this will set the
+ * desired spatial multiplexing powersave mode for a device. Due to
+ * multiple virtual interfaces it might not always be the mode that
+ * actually ends up being used.
+ *
* @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use
*/
@@ -743,6 +748,8 @@ enum nl80211_attrs {
NL80211_ATTR_PMKID,
NL80211_ATTR_MAX_NUM_PMKIDS,
+ NL80211_ATTR_SMPS_MODE,
+
/* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST,
@@ -1442,4 +1449,29 @@ enum nl80211_key_attributes {
NL80211_KEY_MAX = __NL80211_KEY_AFTER_LAST - 1
};
+/**
+ * enum nl80211_smps_mode - spatial multiplexing powersave mode
+ *
+ * @NL80211_SMPS_AUTOMATIC: automatic SM PS, i.e. enable
+ * dynamic SM PS if supported and in powersave, disable
+ * SM PS when powersave is turned off. This is the default
+ * mode for HT devices if they support SM PS.
+ * @NL80211_SMPS_OFF: SM PS turned off, i.e. all chains on
+ * @NL80211_SMPS_STATIC: static SM PS, i.e. using a single chain
+ * @NL80211_SMPS_DYNAMIC: dynamic SM PS, i.e. chains turned on
+ * after, for example, rts/cts handshake
+ * @__NL80211_SMPS_NUM: internal
+ * @NL80211_SMPS_MAX: largest allowed smps value
+ */
+enum nl80211_smps_mode {
+ NL80211_SMPS_AUTOMATIC,
+ NL80211_SMPS_OFF,
+ NL80211_SMPS_STATIC,
+ NL80211_SMPS_DYNAMIC,
+
+ /* keep last */
+ __NL80211_SMPS_NUM,
+ NL80211_SMPS_MAX = __NL80211_SMPS_NUM - 1,
+};
+
#endif /* __LINUX_NL80211_H */
--- wireless-testing.orig/net/wireless/nl80211.c 2009-11-29 11:33:01.000000000 +0100
+++ wireless-testing/net/wireless/nl80211.c 2009-11-29 11:34:11.000000000 +0100
@@ -141,6 +141,7 @@ static struct nla_policy nl80211_policy[
[NL80211_ATTR_4ADDR] = { .type = NLA_U8 },
[NL80211_ATTR_PMKID] = { .type = NLA_BINARY,
.len = WLAN_PMKID_LEN },
+ [NL80211_ATTR_SMPS_MODE] = { .type = NLA_U32 },
};
/* policy for the attributes */
@@ -1052,6 +1053,26 @@ static int nl80211_set_interface(struct
params.use_4addr = -1;
}
+ if (info->attrs[NL80211_ATTR_SMPS_MODE]) {
+ enum nl80211_smps_mode smps_mode;
+
+ smps_mode = nla_get_u32(info->attrs[NL80211_ATTR_SMPS_MODE]);
+
+ if (smps_mode > NL80211_SMPS_MAX) {
+ err = -EINVAL;
+ goto unlock;
+ }
+
+ if (!rdev->ops->set_smps) {
+ err = -EOPNOTSUPP;
+ goto unlock;
+ }
+
+ err = rdev->ops->set_smps(&rdev->wiphy, dev, smps_mode);
+ if (err)
+ goto unlock;
+ }
+
if (info->attrs[NL80211_ATTR_MNTR_FLAGS]) {
if (ntype != NL80211_IFTYPE_MONITOR) {
err = -EINVAL;
--- wireless-testing.orig/include/net/cfg80211.h 2009-11-29 11:33:01.000000000 +0100
+++ wireless-testing/include/net/cfg80211.h 2009-11-29 11:34:11.000000000 +0100
@@ -1115,7 +1115,10 @@ struct cfg80211_ops {
const struct cfg80211_bitrate_mask *mask);
int (*dump_survey)(struct wiphy *wiphy, struct net_device *netdev,
- int idx, struct survey_info *info);
+ int idx, struct survey_info *info);
+
+ int (*set_smps)(struct wiphy *wiphy, struct net_device *netdev,
+ enum nl80211_smps_mode smps_mode);
int (*set_pmksa)(struct wiphy *wiphy, struct net_device *netdev,
struct cfg80211_pmksa *pmksa);
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH 2/2] mac80211: enable spatial multiplexing powersave
2009-11-30 15:38 [PATCH 0/2] Spatial multiplexing powersave (client support) Johannes Berg
2009-11-30 15:38 ` [PATCH 1/2] cfg80211: add SM PS hooks Johannes Berg
@ 2009-11-30 15:38 ` Johannes Berg
2009-11-30 16:54 ` Luis R. Rodriguez
2009-11-30 17:15 ` [PATCH 2/2 v2] " Johannes Berg
2009-12-01 12:37 ` [PATCH v3] " Johannes Berg
2 siblings, 2 replies; 12+ messages in thread
From: Johannes Berg @ 2009-11-30 15:38 UTC (permalink / raw)
To: John Linville; +Cc: linux-wireless
Enable spatial multiplexing in mac80211 by telling the
driver what to do and, where necessary, sending action
frames to the AP to update the requested SMPS mode.
Also includes a trivial implementation for hwsim that
just logs the requested mode.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
---
drivers/net/wireless/mac80211_hwsim.c | 22 ++++++++-
include/linux/ieee80211.h | 25 ++++++++++-
include/net/mac80211.h | 44 +++++++++++++++++++
net/mac80211/cfg.c | 75 ++++++++++++++++++++++++++++++++++
net/mac80211/driver-trace.h | 2
net/mac80211/ht.c | 47 +++++++++++++++++++++
net/mac80211/ieee80211_i.h | 12 +++++
net/mac80211/main.c | 24 ++++++++++
net/mac80211/mlme.c | 63 ++++++++++++++++++++++++++--
net/mac80211/status.c | 38 +++++++++++++++++
net/mac80211/util.c | 74 +++++++++++++++++++++++++++++++++
11 files changed, 418 insertions(+), 8 deletions(-)
--- wireless-testing.orig/include/net/mac80211.h 2009-11-29 11:33:52.000000000 +0100
+++ wireless-testing/include/net/mac80211.h 2009-11-29 11:35:06.000000000 +0100
@@ -597,8 +597,10 @@ enum ieee80211_conf_flags {
* @IEEE80211_CONF_CHANGE_CHANNEL: the channel/channel_type changed
* @IEEE80211_CONF_CHANGE_RETRY_LIMITS: retry limits changed
* @IEEE80211_CONF_CHANGE_IDLE: Idle flag changed
+ * @IEEE80211_CONF_CHANGE_SMPS: Spatial multiplexing powersave mode changed
*/
enum ieee80211_conf_changed {
+ IEEE80211_CONF_CHANGE_SMPS = BIT(1),
IEEE80211_CONF_CHANGE_LISTEN_INTERVAL = BIT(2),
IEEE80211_CONF_CHANGE_MONITOR = BIT(3),
IEEE80211_CONF_CHANGE_PS = BIT(4),
@@ -636,6 +638,10 @@ enum ieee80211_conf_changed {
* @short_frame_max_tx_count: Maximum number of transmissions for a "short"
* frame, called "dot11ShortRetryLimit" in 802.11, but actually means the
* number of transmissions not the number of retries
+ *
+ * @smps_mode: spatial multiplexing powersave mode; note that
+ * %NL80211_SMPS_STATIC is used when the device is not
+ * configured for an HT channel
*/
struct ieee80211_conf {
u32 flags;
@@ -648,6 +654,7 @@ struct ieee80211_conf {
struct ieee80211_channel *channel;
enum nl80211_channel_type channel_type;
+ enum nl80211_smps_mode smps_mode;
};
/**
@@ -930,6 +937,16 @@ enum ieee80211_tkip_key_type {
* @IEEE80211_HW_BEACON_FILTER:
* Hardware supports dropping of irrelevant beacon frames to
* avoid waking up cpu.
+ *
+ * @IEEE80211_HW_SUPPORTS_STATIC_SMPS:
+ * Hardware supports static spatial multiplexing powersave,
+ * ie. can turn off all but one chain even on HT connections
+ * that should be using more chains.
+ *
+ * @IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS:
+ * Hardware supports dynamic spatial multiplexing powersave,
+ * ie. can turn off all but one chain and then wake the rest
+ * up as required after, for example, rts/cts handshake.
*/
enum ieee80211_hw_flags {
IEEE80211_HW_HAS_RATE_CONTROL = 1<<0,
@@ -947,6 +964,8 @@ enum ieee80211_hw_flags {
IEEE80211_HW_SUPPORTS_DYNAMIC_PS = 1<<12,
IEEE80211_HW_MFP_CAPABLE = 1<<13,
IEEE80211_HW_BEACON_FILTER = 1<<14,
+ IEEE80211_HW_SUPPORTS_STATIC_SMPS = 1<<15,
+ IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS = 1<<16,
};
/**
@@ -1215,6 +1234,31 @@ ieee80211_get_alt_retry_rate(const struc
*/
/**
+ * DOC: Spatial multiplexing power save
+ *
+ * SMPS (Spatial multiplexing power save) is a mechanism to conserve
+ * power in an 802.11n implementation. For details on the mechanism
+ * and rationale, please refer to 802.11 (as amended by 802.11n-2009)
+ * "11.2.3 SM power save".
+ *
+ * The mac80211 implementation is capable of sending action frames
+ * to update the AP about the station's SMPS mode, and will instruct
+ * the driver to enter the specific mode. It will also announce the
+ * requested SMPS mode during the association handshake. Hardware
+ * support for this feature is required, and can be indicated by
+ * hardware flags.
+ *
+ * The default mode will be "automatic", which nl80211/cfg80211
+ * defines to be dynamic SMPS in (regular) powersave, and SMPS
+ * turned off otherwise.
+ *
+ * To support this feature, the driver must set the appropriate
+ * hardware support flags, and handle the SMPS flag to the config()
+ * operation. It will then with this mechanism be instructed to
+ * enter the requested SMPS mode while associated to an HT AP.
+ */
+
+/**
* DOC: Frame filtering
*
* mac80211 requires to see many management frames for proper
--- wireless-testing.orig/net/mac80211/ieee80211_i.h 2009-11-29 11:33:29.000000000 +0100
+++ wireless-testing/net/mac80211/ieee80211_i.h 2009-11-29 22:18:08.000000000 +0100
@@ -297,6 +297,8 @@ struct ieee80211_if_managed {
unsigned long timers_running; /* used for quiesce/restart */
bool powersave; /* powersave requested for this iface */
+ enum nl80211_smps_mode req_smps, /* requested smps mode */
+ ap_smps; /* smps mode AP thinks we're in */
unsigned long request;
@@ -587,6 +589,9 @@ struct ieee80211_local {
/* used for uploading changed mc list */
struct work_struct reconfig_filter;
+ /* used to reconfigure hardware SM PS */
+ struct work_struct recalc_smps;
+
/* aggregated multicast list */
struct dev_addr_list *mc_list;
int mc_count;
@@ -760,6 +765,8 @@ struct ieee80211_local {
int user_power_level; /* in dBm */
int power_constr_level; /* in dBm */
+ enum nl80211_smps_mode smps_mode;
+
struct work_struct restart_work;
#ifdef CONFIG_MAC80211_DEBUGFS
@@ -983,6 +990,9 @@ void ieee80211_send_bar(struct ieee80211
void ieee80211_send_delba(struct ieee80211_sub_if_data *sdata,
const u8 *da, u16 tid,
u16 initiator, u16 reason_code);
+int ieee80211_send_smps_action(struct ieee80211_sub_if_data *sdata,
+ enum nl80211_smps_mode smps, const u8 *da,
+ const u8 *bssid);
void ieee80211_sta_stop_rx_ba_session(struct ieee80211_sub_if_data *sdata, u8 *da,
u16 tid, u16 initiator, u16 reason);
@@ -1093,6 +1103,8 @@ void ieee80211_sta_def_wmm_params(struct
u32 ieee80211_sta_get_rates(struct ieee80211_local *local,
struct ieee802_11_elems *elems,
enum ieee80211_band band);
+void ieee80211_recalc_smps(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *forsdata);
#ifdef CONFIG_MAC80211_NOINLINE
#define debug_noinline noinline
--- wireless-testing.orig/net/mac80211/cfg.c 2009-11-29 11:33:00.000000000 +0100
+++ wireless-testing/net/mac80211/cfg.c 2009-11-29 11:40:29.000000000 +0100
@@ -1316,6 +1316,54 @@ static int ieee80211_testmode_cmd(struct
}
#endif
+static int ieee80211_request_smps(struct ieee80211_sub_if_data *sdata,
+ enum nl80211_smps_mode smps_mode)
+{
+ const u8 *ap;
+ enum nl80211_smps_mode old_req;
+ int err = 0;
+
+ mutex_lock(&sdata->u.mgd.mtx);
+
+ old_req = sdata->u.mgd.req_smps;
+ sdata->u.mgd.req_smps = smps_mode;
+
+ if (old_req == smps_mode &&
+ smps_mode != NL80211_SMPS_AUTOMATIC)
+ goto out;
+
+ /*
+ * If not associated, or current sasociation is not an HT
+ * association, there's no need to send an action frame.
+ */
+ if (!sdata->u.mgd.associated ||
+ sdata->local->oper_channel_type == NL80211_CHAN_NO_HT) {
+ mutex_lock(&sdata->local->iflist_mtx);
+ ieee80211_recalc_smps(sdata->local, sdata);
+ mutex_unlock(&sdata->local->iflist_mtx);
+ goto out;
+ }
+
+ ap = sdata->u.mgd.associated->cbss.bssid;
+
+ if (smps_mode == NL80211_SMPS_AUTOMATIC) {
+ if (sdata->u.mgd.powersave)
+ smps_mode = NL80211_SMPS_DYNAMIC;
+ else
+ smps_mode = NL80211_SMPS_OFF;
+ }
+
+ /* send SM PS frame to AP */
+ err = ieee80211_send_smps_action(sdata, smps_mode,
+ ap, ap);
+ if (err)
+ sdata->u.mgd.req_smps = old_req;
+ out:
+ mutex_unlock(&sdata->u.mgd.mtx);
+
+ return err;
+}
+
static int ieee80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev,
bool enabled, int timeout)
{
@@ -1333,6 +1381,9 @@ static int ieee80211_set_power_mgmt(stru
sdata->u.mgd.powersave = enabled;
conf->dynamic_ps_timeout = timeout;
+ /* no change, but if automatic follow powersave */
+ ieee80211_request_smps(sdata, sdata->u.mgd.req_smps);
+
if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS)
ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
@@ -1383,6 +1434,29 @@ static int ieee80211_set_bitrate_mask(st
return err;
}
+static int ieee80211_set_smps(struct wiphy *wiphy, struct net_device *dev,
+ enum nl80211_smps_mode smps_mode)
+{
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct ieee80211_local *local = sdata->local;
+
+ if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_STATIC_SMPS) &&
+ smps_mode == NL80211_SMPS_STATIC)
+ return -EINVAL;
+
+ /* auto should be dynamic if in PS mode */
+ if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS) &&
+ (smps_mode == NL80211_SMPS_DYNAMIC ||
+ smps_mode == NL80211_SMPS_AUTOMATIC))
+ return -EINVAL;
+
+ /* supported only on managed interfaces for now */
+ if (sdata->vif.type != NL80211_IFTYPE_STATION)
+ return -EOPNOTSUPP;
+
+ return ieee80211_request_smps(sdata, smps_mode);
+}
+
struct cfg80211_ops mac80211_config_ops = {
.add_virtual_intf = ieee80211_add_iface,
.del_virtual_intf = ieee80211_del_iface,
@@ -1429,4 +1503,5 @@ struct cfg80211_ops mac80211_config_ops
CFG80211_TESTMODE_CMD(ieee80211_testmode_cmd)
.set_power_mgmt = ieee80211_set_power_mgmt,
.set_bitrate_mask = ieee80211_set_bitrate_mask,
+ .set_smps = ieee80211_set_smps,
};
--- wireless-testing.orig/include/linux/ieee80211.h 2009-11-29 11:33:00.000000000 +0100
+++ wireless-testing/include/linux/ieee80211.h 2009-11-29 11:35:06.000000000 +0100
@@ -707,6 +707,10 @@ struct ieee80211_mgmt {
u8 action;
u8 trans_id[WLAN_SA_QUERY_TR_ID_LEN];
} __attribute__ ((packed)) sa_query;
+ struct {
+ u8 action;
+ u8 smps_control;
+ } __attribute__ ((packed)) ht_smps;
} u;
} __attribute__ ((packed)) action;
} u;
@@ -824,6 +828,7 @@ struct ieee80211_ht_cap {
#define IEEE80211_HT_CAP_LDPC_CODING 0x0001
#define IEEE80211_HT_CAP_SUP_WIDTH_20_40 0x0002
#define IEEE80211_HT_CAP_SM_PS 0x000C
+#define IEEE80211_HT_CAP_SM_PS_SHIFT 2
#define IEEE80211_HT_CAP_GRN_FLD 0x0010
#define IEEE80211_HT_CAP_SGI_20 0x0020
#define IEEE80211_HT_CAP_SGI_40 0x0040
@@ -839,6 +844,7 @@ struct ieee80211_ht_cap {
/* 802.11n HT capability AMPDU settings (for ampdu_params_info) */
#define IEEE80211_HT_AMPDU_PARM_FACTOR 0x03
#define IEEE80211_HT_AMPDU_PARM_DENSITY 0x1C
+#define IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT 2
/*
* Maximum length of AMPDU that the STA can receive.
@@ -922,12 +928,17 @@ struct ieee80211_ht_info {
#define IEEE80211_MAX_AMPDU_BUF 0x40
-/* Spatial Multiplexing Power Save Modes */
+/* Spatial Multiplexing Power Save Modes (for capability) */
#define WLAN_HT_CAP_SM_PS_STATIC 0
#define WLAN_HT_CAP_SM_PS_DYNAMIC 1
#define WLAN_HT_CAP_SM_PS_INVALID 2
#define WLAN_HT_CAP_SM_PS_DISABLED 3
+/* for SM power control field lower two bits */
+#define WLAN_HT_SMPS_CONTROL_DISABLED 0
+#define WLAN_HT_SMPS_CONTROL_STATIC 1
+#define WLAN_HT_SMPS_CONTROL_DYNAMIC 3
+
/* Authentication algorithms */
#define WLAN_AUTH_OPEN 0
#define WLAN_AUTH_SHARED_KEY 1
@@ -1150,6 +1161,18 @@ enum ieee80211_spectrum_mgmt_actioncode
WLAN_ACTION_SPCT_CHL_SWITCH = 4,
};
+/* HT action codes */
+enum ieee80211_ht_actioncode {
+ WLAN_HT_ACTION_NOTIFY_CHANWIDTH = 0,
+ WLAN_HT_ACTION_SMPS = 1,
+ WLAN_HT_ACTION_PSMP = 2,
+ WLAN_HT_ACTION_PCO_PHASE = 3,
+ WLAN_HT_ACTION_CSI = 4,
+ WLAN_HT_ACTION_NONCOMPRESSED_BF = 5,
+ WLAN_HT_ACTION_COMPRESSED_BF = 6,
+ WLAN_HT_ACTION_ASEL_IDX_FEEDBACK = 7,
+};
+
/* Security key length */
enum ieee80211_key_len {
WLAN_KEY_LEN_WEP40 = 5,
--- wireless-testing.orig/net/mac80211/mlme.c 2009-11-29 11:33:47.000000000 +0100
+++ wireless-testing/net/mac80211/mlme.c 2009-11-29 22:18:09.000000000 +0100
@@ -398,6 +398,8 @@ static void ieee80211_send_assoc(struct
__le16 tmp;
u32 flags = local->hw.conf.channel->flags;
+ /* determine capability flags */
+
switch (ht_info->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
if (flags & IEEE80211_CHAN_NO_HT40PLUS) {
@@ -413,17 +415,64 @@ static void ieee80211_send_assoc(struct
break;
}
- tmp = cpu_to_le16(cap);
- pos = skb_put(skb, sizeof(struct ieee80211_ht_cap)+2);
+ /* set SM PS mode properly */
+ cap &= ~IEEE80211_HT_CAP_SM_PS;
+ /* new association always uses requested smps mode */
+ if (ifmgd->req_smps == NL80211_SMPS_AUTOMATIC) {
+ if (ifmgd->powersave)
+ ifmgd->ap_smps = NL80211_SMPS_DYNAMIC;
+ else
+ ifmgd->ap_smps = NL80211_SMPS_OFF;
+ } else
+ ifmgd->ap_smps = ifmgd->req_smps;
+
+ switch (ifmgd->ap_smps) {
+ case NL80211_SMPS_AUTOMATIC:
+ case __NL80211_SMPS_NUM:
+ WARN_ON(1);
+ case NL80211_SMPS_OFF:
+ cap |= WLAN_HT_CAP_SM_PS_DISABLED <<
+ IEEE80211_HT_CAP_SM_PS_SHIFT;
+ break;
+ case NL80211_SMPS_STATIC:
+ cap |= WLAN_HT_CAP_SM_PS_STATIC <<
+ IEEE80211_HT_CAP_SM_PS_SHIFT;
+ break;
+ case NL80211_SMPS_DYNAMIC:
+ cap |= WLAN_HT_CAP_SM_PS_DYNAMIC <<
+ IEEE80211_HT_CAP_SM_PS_SHIFT;
+ break;
+ }
+
+ /* reserve and fill IE */
+
+ pos = skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2);
*pos++ = WLAN_EID_HT_CAPABILITY;
*pos++ = sizeof(struct ieee80211_ht_cap);
memset(pos, 0, sizeof(struct ieee80211_ht_cap));
+
+ /* capability flags */
+ tmp = cpu_to_le16(cap);
memcpy(pos, &tmp, sizeof(u16));
pos += sizeof(u16);
- /* TODO: needs a define here for << 2 */
+
+ /* AMPDU parameters */
*pos++ = sband->ht_cap.ampdu_factor |
- (sband->ht_cap.ampdu_density << 2);
+ (sband->ht_cap.ampdu_density <<
+ IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT);
+
+ /* MCS set */
memcpy(pos, &sband->ht_cap.mcs, sizeof(sband->ht_cap.mcs));
+ pos += sizeof(sband->ht_cap.mcs);
+
+ /* extended capabilities */
+ pos += sizeof(__le16);
+
+ /* BF capabilities */
+ pos += sizeof(__le32);
+
+ /* antenna selection */
+ pos += sizeof(u8);
}
IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
@@ -932,6 +981,7 @@ static void ieee80211_set_associated(str
mutex_lock(&local->iflist_mtx);
ieee80211_recalc_ps(local, -1);
+ ieee80211_recalc_smps(local, sdata);
mutex_unlock(&local->iflist_mtx);
netif_start_queue(sdata->dev);
@@ -2328,6 +2378,11 @@ void ieee80211_sta_setup_sdata(struct ie
ifmgd->flags |= IEEE80211_STA_WMM_ENABLED;
mutex_init(&ifmgd->mtx);
+
+ if (sdata->local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS)
+ ifmgd->req_smps = NL80211_SMPS_AUTOMATIC;
+ else
+ ifmgd->req_smps = NL80211_SMPS_OFF;
}
/* scan finished notification */
--- wireless-testing.orig/net/mac80211/ht.c 2009-11-29 11:33:29.000000000 +0100
+++ wireless-testing/net/mac80211/ht.c 2009-11-29 11:35:06.000000000 +0100
@@ -166,3 +166,50 @@ void ieee80211_process_delba(struct ieee
spin_unlock_bh(&sta->lock);
}
}
+
+int ieee80211_send_smps_action(struct ieee80211_sub_if_data *sdata,
+ enum nl80211_smps_mode smps, const u8 *da,
+ const u8 *bssid)
+{
+ struct ieee80211_local *local = sdata->local;
+ struct sk_buff *skb;
+ struct ieee80211_mgmt *action_frame;
+
+ /* 27 = header + category + action + smps mode */
+ skb = dev_alloc_skb(27 + local->hw.extra_tx_headroom);
+ if (!skb)
+ return -ENOMEM;
+
+ skb_reserve(skb, local->hw.extra_tx_headroom);
+ action_frame = (void *)skb_put(skb, 27);
+ memcpy(action_frame->da, da, ETH_ALEN);
+ memcpy(action_frame->sa, sdata->dev->dev_addr, ETH_ALEN);
+ memcpy(action_frame->bssid, bssid, ETH_ALEN);
+ action_frame->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
+ IEEE80211_STYPE_ACTION);
+ action_frame->u.action.category = WLAN_CATEGORY_HT;
+ action_frame->u.action.u.ht_smps.action = WLAN_HT_ACTION_SMPS;
+ switch (smps) {
+ case NL80211_SMPS_AUTOMATIC:
+ case __NL80211_SMPS_NUM:
+ WARN_ON(1);
+ case NL80211_SMPS_OFF:
+ action_frame->u.action.u.ht_smps.smps_control =
+ WLAN_HT_SMPS_CONTROL_DISABLED;
+ break;
+ case NL80211_SMPS_STATIC:
+ action_frame->u.action.u.ht_smps.smps_control =
+ WLAN_HT_SMPS_CONTROL_STATIC;
+ break;
+ case NL80211_SMPS_DYNAMIC:
+ action_frame->u.action.u.ht_smps.smps_control =
+ WLAN_HT_SMPS_CONTROL_DYNAMIC;
+ break;
+ }
+
+ /* we'll do more on status of this frame */
+ IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS;
+ ieee80211_tx_skb(sdata, skb);
+
+ return 0;
+}
--- wireless-testing.orig/net/mac80211/status.c 2009-11-29 11:33:00.000000000 +0100
+++ wireless-testing/net/mac80211/status.c 2009-11-29 11:35:06.000000000 +0100
@@ -134,6 +134,40 @@ static void ieee80211_handle_filtered_fr
dev_kfree_skb(skb);
}
+static void ieee80211_frame_acked(struct sta_info *sta, struct sk_buff *skb)
+{
+ struct ieee80211_mgmt *mgmt = (void *) skb->data;
+ struct ieee80211_local *local = sta->local;
+ struct ieee80211_sub_if_data *sdata = sta->sdata;
+
+ if (ieee80211_is_action(mgmt->frame_control) &&
+ sdata->vif.type == NL80211_IFTYPE_STATION &&
+ mgmt->u.action.category == WLAN_CATEGORY_HT &&
+ mgmt->u.action.u.ht_smps.action == WLAN_HT_ACTION_SMPS) {
+ /*
+ * This update looks racy, but isn't -- if we come
+ * here we've definitely got a station that we're
+ * talking to, and on a managed interface that can
+ * only be the AP. And the only other place updating
+ * this variable is before we're associated.
+ */
+ switch (mgmt->u.action.u.ht_smps.smps_control) {
+ case WLAN_HT_SMPS_CONTROL_DYNAMIC:
+ sta->sdata->u.mgd.ap_smps = NL80211_SMPS_DYNAMIC;
+ break;
+ case WLAN_HT_SMPS_CONTROL_STATIC:
+ sta->sdata->u.mgd.ap_smps = NL80211_SMPS_STATIC;
+ break;
+ case WLAN_HT_SMPS_CONTROL_DISABLED:
+ default: /* shouldn't happen since we don't send that */
+ sta->sdata->u.mgd.ap_smps = NL80211_SMPS_OFF;
+ break;
+ }
+
+ ieee80211_queue_work(&local->hw, &local->recalc_smps);
+ }
+}
+
void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
{
struct sk_buff *skb2;
@@ -209,6 +243,10 @@ void ieee80211_tx_status(struct ieee8021
rate_control_tx_status(local, sband, sta, skb);
if (ieee80211_vif_is_mesh(&sta->sdata->vif))
ieee80211s_update_metric(local, sta, skb);
+
+ if (!(info->flags & IEEE80211_TX_CTL_INJECTED) &&
+ (info->flags & IEEE80211_TX_STAT_ACK))
+ ieee80211_frame_acked(sta, skb);
}
rcu_read_unlock();
--- wireless-testing.orig/net/mac80211/main.c 2009-11-29 11:33:00.000000000 +0100
+++ wireless-testing/net/mac80211/main.c 2009-11-29 22:18:07.000000000 +0100
@@ -113,6 +113,18 @@ int ieee80211_hw_config(struct ieee80211
changed |= IEEE80211_CONF_CHANGE_CHANNEL;
}
+ if (!conf_is_ht(&local->hw.conf)) {
+ /*
+ * mac80211.h documents that this is only valid
+ * when the channel is set to an HT type, and
+ * that otherwise STATIC is used.
+ */
+ local->hw.conf.smps_mode = NL80211_SMPS_STATIC;
+ } else if (local->hw.conf.smps_mode != local->smps_mode) {
+ local->hw.conf.smps_mode = local->smps_mode;
+ changed |= IEEE80211_CONF_CHANGE_SMPS;
+ }
+
if (scan_chan)
power = chan->max_power;
else
@@ -297,6 +309,16 @@ void ieee80211_restart_hw(struct ieee802
}
EXPORT_SYMBOL(ieee80211_restart_hw);
+static void ieee80211_recalc_smps_work(struct work_struct *work)
+{
+ struct ieee80211_local *local =
+ container_of(work, struct ieee80211_local, recalc_smps);
+
+ mutex_lock(&local->iflist_mtx);
+ ieee80211_recalc_smps(local, NULL);
+ mutex_unlock(&local->iflist_mtx);
+}
+
struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
const struct ieee80211_ops *ops)
{
@@ -370,6 +392,8 @@ struct ieee80211_hw *ieee80211_alloc_hw(
INIT_WORK(&local->restart_work, ieee80211_restart_work);
INIT_WORK(&local->reconfig_filter, ieee80211_reconfig_filter);
+ INIT_WORK(&local->recalc_smps, ieee80211_recalc_smps_work);
+ local->smps_mode = NL80211_SMPS_OFF;
INIT_WORK(&local->dynamic_ps_enable_work,
ieee80211_dynamic_ps_enable_work);
--- wireless-testing.orig/net/mac80211/util.c 2009-11-29 11:33:00.000000000 +0100
+++ wireless-testing/net/mac80211/util.c 2009-11-29 11:35:06.000000000 +0100
@@ -1170,3 +1170,77 @@ int ieee80211_reconfig(struct ieee80211_
return 0;
}
+static int check_mgd_smps(struct ieee80211_if_managed *ifmgd,
+ enum nl80211_smps_mode *smps_mode)
+{
+ if (ifmgd->associated) {
+ *smps_mode = ifmgd->ap_smps;
+
+ if (smps_mode == NL80211_SMPS_AUTOMATIC) {
+ if (ifmgd->powersave)
+ *smps_mode = NL80211_SMPS_DYNAMIC;
+ else
+ *smps_mode = NL80211_SMPS_OFF;
+ }
+
+ return 1;
+ }
+
+ return 0;
+}
+
+/* must hold iflist_mtx */
+void ieee80211_recalc_smps(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *forsdata)
+{
+ struct ieee80211_sub_if_data *sdata;
+ enum nl80211_smps_mode smps_mode = NL80211_SMPS_OFF;
+ int count = 0;
+
+ if (forsdata)
+ WARN_ON(!mutex_is_locked(&forsdata->u.mgd.mtx));
+
+ WARN_ON(!mutex_is_locked(&local->iflist_mtx));
+
+ /*
+ * This function could be improved to handle multiple
+ * interfaces better, but right now it makes any
+ * non-station interfaces force SM PS to be turned
+ * off. If there are multiple station interfaces it
+ * could also use the best possible mode, e.g. if
+ * one is in static and the other in dynamic then
+ * dynamic is ok.
+ */
+
+ list_for_each_entry(sdata, &local->interfaces, list) {
+ if (!netif_running(sdata->dev))
+ continue;
+ if (sdata->vif.type != NL80211_IFTYPE_STATION)
+ goto set;
+ if (sdata != forsdata) {
+ /*
+ * This nested is ok -- we are holding the iflist_mtx
+ * so can't get here twice or so. But it's required
+ * since normally we acquire it first and then the
+ * iflist_mtx.
+ */
+ mutex_lock_nested(&sdata->u.mgd.mtx, SINGLE_DEPTH_NESTING);
+ count += check_mgd_smps(&sdata->u.mgd, &smps_mode);
+ mutex_unlock(&sdata->u.mgd.mtx);
+ } else
+ count += check_mgd_smps(&sdata->u.mgd, &smps_mode);
+
+ if (count > 1) {
+ smps_mode = NL80211_SMPS_OFF;
+ break;
+ }
+ }
+
+ if (smps_mode == local->smps_mode)
+ return;
+
+ set:
+ local->smps_mode = smps_mode;
+ /* changed flag is auto-detected for this */
+ ieee80211_hw_config(local, 0);
+}
--- wireless-testing.orig/drivers/net/wireless/mac80211_hwsim.c 2009-11-29 11:33:52.000000000 +0100
+++ wireless-testing/drivers/net/wireless/mac80211_hwsim.c 2009-11-29 11:35:06.000000000 +0100
@@ -619,12 +619,26 @@ static int mac80211_hwsim_config(struct
{
struct mac80211_hwsim_data *data = hw->priv;
struct ieee80211_conf *conf = &hw->conf;
+ static const char *chantypes[4] = {
+ [NL80211_CHAN_NO_HT] = "noht",
+ [NL80211_CHAN_HT20] = "ht20",
+ [NL80211_CHAN_HT40MINUS] = "ht40-",
+ [NL80211_CHAN_HT40PLUS] = "ht40+",
+ };
+ static const char *smps_modes[__NL80211_SMPS_NUM] = {
+ [NL80211_SMPS_AUTOMATIC] = "auto/invalid",
+ [NL80211_SMPS_OFF] = "off",
+ [NL80211_SMPS_STATIC] = "static",
+ [NL80211_SMPS_DYNAMIC] = "dynamic",
+ };
- printk(KERN_DEBUG "%s:%s (freq=%d idle=%d ps=%d)\n",
+ printk(KERN_DEBUG "%s:%s (freq=%d/%s idle=%d ps=%d smps=%s)\n",
wiphy_name(hw->wiphy), __func__,
conf->channel->center_freq,
+ chantypes[conf->channel_type],
!!(conf->flags & IEEE80211_CONF_IDLE),
- !!(conf->flags & IEEE80211_CONF_PS));
+ !!(conf->flags & IEEE80211_CONF_PS),
+ smps_modes[conf->smps_mode]);
data->idle = !!(conf->flags & IEEE80211_CONF_IDLE);
@@ -1121,7 +1135,9 @@ static int __init init_mac80211_hwsim(vo
BIT(NL80211_IFTYPE_MESH_POINT);
hw->flags = IEEE80211_HW_MFP_CAPABLE |
- IEEE80211_HW_SIGNAL_DBM;
+ IEEE80211_HW_SIGNAL_DBM |
+ IEEE80211_HW_SUPPORTS_STATIC_SMPS |
+ IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS;
/* ask mac80211 to reserve space for magic */
hw->vif_data_size = sizeof(struct hwsim_vif_priv);
--- wireless-testing.orig/net/mac80211/driver-trace.h 2009-11-29 22:18:24.000000000 +0100
+++ wireless-testing/net/mac80211/driver-trace.h 2009-11-29 22:19:04.000000000 +0100
@@ -140,6 +140,7 @@ TRACE_EVENT(drv_config,
__field(u8, short_frame_max_tx_count)
__field(int, center_freq)
__field(int, channel_type)
+ __field(int, smps)
),
TP_fast_assign(
@@ -155,6 +156,7 @@ TRACE_EVENT(drv_config,
__entry->short_frame_max_tx_count = local->hw.conf.short_frame_max_tx_count;
__entry->center_freq = local->hw.conf.channel->center_freq;
__entry->channel_type = local->hw.conf.channel_type;
+ __entry->smps = local->hw.conf.smps_mode;
),
TP_printk(
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 2/2] mac80211: enable spatial multiplexing powersave
2009-11-30 15:38 ` [PATCH 2/2] mac80211: enable spatial multiplexing powersave Johannes Berg
@ 2009-11-30 16:54 ` Luis R. Rodriguez
2009-11-30 17:04 ` Johannes Berg
2009-11-30 17:15 ` [PATCH 2/2 v2] " Johannes Berg
1 sibling, 1 reply; 12+ messages in thread
From: Luis R. Rodriguez @ 2009-11-30 16:54 UTC (permalink / raw)
To: Johannes Berg; +Cc: John Linville, linux-wireless
> --- wireless-testing.orig/net/mac80211/cfg.c 2009-11-29 11:33:00.000000000 +0100
> +++ wireless-testing/net/mac80211/cfg.c 2009-11-29 11:40:29.000000000 +0100
> @@ -1316,6 +1316,54 @@ static int ieee80211_testmode_cmd(struct
> }
> #endif
>
> +static int ieee80211_request_smps(struct ieee80211_sub_if_data *sdata,
> + enum nl80211_smps_mode smps_mode)
> +{
> + const u8 *ap;
> + enum nl80211_smps_mode old_req;
> + int err = 0;
> +
> + mutex_lock(&sdata->u.mgd.mtx);
> +
> + old_req = sdata->u.mgd.req_smps;
> + sdata->u.mgd.req_smps = smps_mode;
sdata->u.mgd.mtx seems protected by sdata->u.mgd.mtx.
> +
> + if (old_req == smps_mode &&
> + smps_mode != NL80211_SMPS_AUTOMATIC)
> + goto out;
> +
> + /*
> + * If not associated, or current sasociation is not an HT
> + * association, there's no need to send an action frame.
> + */
> + if (!sdata->u.mgd.associated ||
> + sdata->local->oper_channel_type == NL80211_CHAN_NO_HT) {
> + mutex_lock(&sdata->local->iflist_mtx);
> + ieee80211_recalc_smps(sdata->local, sdata);
> + mutex_unlock(&sdata->local->iflist_mtx);
> + goto out;
> + }
> +
> + ap = sdata->u.mgd.associated->cbss.bssid;
> +
> + if (smps_mode == NL80211_SMPS_AUTOMATIC) {
> + if (sdata->u.mgd.powersave)
> + smps_mode = NL80211_SMPS_DYNAMIC;
> + else
> + smps_mode = NL80211_SMPS_OFF;
> + }
> +
> + /* send SM PS frame to AP */
> + err = ieee80211_send_smps_action(sdata, smps_mode,
> + ap, ap);
> + if (err)
> + sdata->u.mgd.req_smps = old_req;
> + out:
> + mutex_unlock(&sdata->u.mgd.mtx);
> +
> + return err;
> +}
> static int ieee80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev,
> bool enabled, int timeout)
> {
> @@ -1333,6 +1381,9 @@ static int ieee80211_set_power_mgmt(stru
> sdata->u.mgd.powersave = enabled;
> conf->dynamic_ps_timeout = timeout;
>
> + /* no change, but if automatic follow powersave */
> + ieee80211_request_smps(sdata, sdata->u.mgd.req_smps);
But yet the caller uses it without any locking. Seems racy between
here and the respective mutex_lock().
> @@ -932,6 +981,7 @@ static void ieee80211_set_associated(str
>
> mutex_lock(&local->iflist_mtx);
> ieee80211_recalc_ps(local, -1);
> + ieee80211_recalc_smps(local, sdata);
> mutex_unlock(&local->iflist_mtx);
>
> netif_start_queue(sdata->dev);
> @@ -2328,6 +2378,11 @@ void ieee80211_sta_setup_sdata(struct ie
> ifmgd->flags |= IEEE80211_STA_WMM_ENABLED;
>
> mutex_init(&ifmgd->mtx);
> +
> + if (sdata->local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS)
> + ifmgd->req_smps = NL80211_SMPS_AUTOMATIC;
> + else
> + ifmgd->req_smps = NL80211_SMPS_OFF;
How about just checking the HT cap as well and if no HT cap has been
set warn as it would indicate a driver bug actually.
> --- wireless-testing.orig/net/mac80211/status.c 2009-11-29 11:33:00.000000000 +0100
> +++ wireless-testing/net/mac80211/status.c 2009-11-29 11:35:06.000000000 +0100
> @@ -134,6 +134,40 @@ static void ieee80211_handle_filtered_fr
> dev_kfree_skb(skb);
> }
>
> +static void ieee80211_frame_acked(struct sta_info *sta, struct sk_buff *skb)
> +{
> + struct ieee80211_mgmt *mgmt = (void *) skb->data;
> + struct ieee80211_local *local = sta->local;
> + struct ieee80211_sub_if_data *sdata = sta->sdata;
> +
> + if (ieee80211_is_action(mgmt->frame_control) &&
> + sdata->vif.type == NL80211_IFTYPE_STATION &&
> + mgmt->u.action.category == WLAN_CATEGORY_HT &&
> + mgmt->u.action.u.ht_smps.action == WLAN_HT_ACTION_SMPS) {
> + /*
> + * This update looks racy, but isn't -- if we come
> + * here we've definitely got a station that we're
> + * talking to, and on a managed interface that can
> + * only be the AP. And the only other place updating
> + * this variable is before we're associated.
> + */
> + switch (mgmt->u.action.u.ht_smps.smps_control) {
> + case WLAN_HT_SMPS_CONTROL_DYNAMIC:
> + sta->sdata->u.mgd.ap_smps = NL80211_SMPS_DYNAMIC;
> + break;
> + case WLAN_HT_SMPS_CONTROL_STATIC:
> + sta->sdata->u.mgd.ap_smps = NL80211_SMPS_STATIC;
> + break;
> + case WLAN_HT_SMPS_CONTROL_DISABLED:
> + default: /* shouldn't happen since we don't send that */
> + sta->sdata->u.mgd.ap_smps = NL80211_SMPS_OFF;
> + break;
> + }
> +
> + ieee80211_queue_work(&local->hw, &local->recalc_smps);
OK from what I gather so far we just queue work if we received an ACK
from the action frame we sent. Since we tell the device to
ieee80211_stop_device() the device should have stopped RX'ing and that
*should* mean preventing processing future tx status reports. We'll
find out if that assumption is wrong.
> + }
> +}
> +
> void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
> {
> struct sk_buff *skb2;
> @@ -209,6 +243,10 @@ void ieee80211_tx_status(struct ieee8021
> rate_control_tx_status(local, sband, sta, skb);
> if (ieee80211_vif_is_mesh(&sta->sdata->vif))
> ieee80211s_update_metric(local, sta, skb);
> +
> + if (!(info->flags & IEEE80211_TX_CTL_INJECTED) &&
> + (info->flags & IEEE80211_TX_STAT_ACK))
> + ieee80211_frame_acked(sta, skb);
> }
>
> rcu_read_unlock();
Nice, should the nullfunc ack check be handled here as well?
> --- wireless-testing.orig/net/mac80211/main.c 2009-11-29 11:33:00.000000000 +0100
> +++ wireless-testing/net/mac80211/main.c 2009-11-29 22:18:07.000000000 +0100
> @@ -113,6 +113,18 @@ int ieee80211_hw_config(struct ieee80211
> changed |= IEEE80211_CONF_CHANGE_CHANNEL;
> }
>
> + if (!conf_is_ht(&local->hw.conf)) {
> + /*
> + * mac80211.h documents that this is only valid
> + * when the channel is set to an HT type, and
> + * that otherwise STATIC is used.
> + */
> + local->hw.conf.smps_mode = NL80211_SMPS_STATIC;
Why wouldn't this just be SMPS_OFF ?
> --- wireless-testing.orig/net/mac80211/util.c 2009-11-29 11:33:00.000000000 +0100
> +++ wireless-testing/net/mac80211/util.c 2009-11-29 11:35:06.000000000 +0100
> @@ -1170,3 +1170,77 @@ int ieee80211_reconfig(struct ieee80211_
> return 0;
> }
>
> +static int check_mgd_smps(struct ieee80211_if_managed *ifmgd,
> + enum nl80211_smps_mode *smps_mode)
> +{
> + if (ifmgd->associated) {
> + *smps_mode = ifmgd->ap_smps;
> +
> + if (smps_mode == NL80211_SMPS_AUTOMATIC) {
This should be *smps_mode.
Luis
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 2/2] mac80211: enable spatial multiplexing powersave
2009-11-30 16:54 ` Luis R. Rodriguez
@ 2009-11-30 17:04 ` Johannes Berg
2009-11-30 17:22 ` Luis R. Rodriguez
0 siblings, 1 reply; 12+ messages in thread
From: Johannes Berg @ 2009-11-30 17:04 UTC (permalink / raw)
To: Luis R. Rodriguez; +Cc: John Linville, linux-wireless
[-- Attachment #1: Type: text/plain, Size: 6359 bytes --]
On Mon, 2009-11-30 at 08:54 -0800, Luis R. Rodriguez wrote:
> > +static int ieee80211_request_smps(struct ieee80211_sub_if_data *sdata,
> > + enum nl80211_smps_mode smps_mode)
> > +{
> > + const u8 *ap;
> > + enum nl80211_smps_mode old_req;
> > + int err = 0;
> > +
> > + mutex_lock(&sdata->u.mgd.mtx);
> > +
> > + old_req = sdata->u.mgd.req_smps;
> > + sdata->u.mgd.req_smps = smps_mode;
>
> sdata->u.mgd.mtx seems protected by sdata->u.mgd.mtx.
You mean req_smps, I gather.
> > +
> > + if (old_req == smps_mode &&
> > + smps_mode != NL80211_SMPS_AUTOMATIC)
> > + goto out;
> > +
> > + /*
> > + * If not associated, or current sasociation is not an HT
> > + * association, there's no need to send an action frame.
> > + */
> > + if (!sdata->u.mgd.associated ||
> > + sdata->local->oper_channel_type == NL80211_CHAN_NO_HT) {
> > + mutex_lock(&sdata->local->iflist_mtx);
> > + ieee80211_recalc_smps(sdata->local, sdata);
> > + mutex_unlock(&sdata->local->iflist_mtx);
> > + goto out;
> > + }
> > +
> > + ap = sdata->u.mgd.associated->cbss.bssid;
> > +
> > + if (smps_mode == NL80211_SMPS_AUTOMATIC) {
> > + if (sdata->u.mgd.powersave)
> > + smps_mode = NL80211_SMPS_DYNAMIC;
> > + else
> > + smps_mode = NL80211_SMPS_OFF;
> > + }
> > +
> > + /* send SM PS frame to AP */
> > + err = ieee80211_send_smps_action(sdata, smps_mode,
> > + ap, ap);
> > + if (err)
> > + sdata->u.mgd.req_smps = old_req;
> > + out:
> > + mutex_unlock(&sdata->u.mgd.mtx);
> > +
> > + return err;
> > +}
> > static int ieee80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev,
> > bool enabled, int timeout)
> > {
> > @@ -1333,6 +1381,9 @@ static int ieee80211_set_power_mgmt(stru
> > sdata->u.mgd.powersave = enabled;
> > conf->dynamic_ps_timeout = timeout;
> >
> > + /* no change, but if automatic follow powersave */
> > + ieee80211_request_smps(sdata, sdata->u.mgd.req_smps);
>
> But yet the caller uses it without any locking. Seems racy between
> here and the respective mutex_lock().
Hmm, good catch. Although I think it doesn't matter since this is the
only code path that touches it apart from set_smps, and they both occur
under rtnl.
> > mutex_init(&ifmgd->mtx);
> > +
> > + if (sdata->local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS)
> > + ifmgd->req_smps = NL80211_SMPS_AUTOMATIC;
> > + else
> > + ifmgd->req_smps = NL80211_SMPS_OFF;
>
> How about just checking the HT cap as well and if no HT cap has been
> set warn as it would indicate a driver bug actually.
You mean if it sets dynps w/o HT cap? We /could/ do that, but I don't
really see a reason for it. We'd find out soon enough by a non-HT driver
supporting SMPS etc.
> > + ieee80211_queue_work(&local->hw, &local->recalc_smps);
>
> OK from what I gather so far we just queue work if we received an ACK
> from the action frame we sent. Since we tell the device to
> ieee80211_stop_device() the device should have stopped RX'ing and that
> *should* mean preventing processing future tx status reports. We'll
> find out if that assumption is wrong.
It's not about receiving -- only about transmitting. But yeah, we'll
find out. I have a patch pending that'll flush queues etc., that would
make it completely impossible if implemented by the driver, but I don't
think we need to worry about it.
> > @@ -209,6 +243,10 @@ void ieee80211_tx_status(struct ieee8021
> > rate_control_tx_status(local, sband, sta, skb);
> > if (ieee80211_vif_is_mesh(&sta->sdata->vif))
> > ieee80211s_update_metric(local, sta, skb);
> > +
> > + if (!(info->flags & IEEE80211_TX_CTL_INJECTED) &&
> > + (info->flags & IEEE80211_TX_STAT_ACK))
> > + ieee80211_frame_acked(sta, skb);
> > }
> >
> > rcu_read_unlock();
>
> Nice, should the nullfunc ack check be handled here as well?
Well, it could, in theory. But I've now imposed a requirement that SMPS
capable devices also implement status notifications properly, which may
or may not be possible for all frames. In fact, we may need to change
this in the future but for now I prefer this requirement -- but for HT
only.
> > + if (!conf_is_ht(&local->hw.conf)) {
> > + /*
> > + * mac80211.h documents that this is only valid
> > + * when the channel is set to an HT type, and
> > + * that otherwise STATIC is used.
> > + */
> > + local->hw.conf.smps_mode = NL80211_SMPS_STATIC;
>
> Why wouldn't this just be SMPS_OFF ?
Well, it doesn't matter either way. However, it was easier this way for
iwlwifi because ultimately we want to turn off all but one chain if
non-HT and/or static SMPS. So at least there it made sense to use STATIC
here to avoid the extra check. But other drivers may or may not want to
ignore it completely in the HT case. I've documented that it sets it to
STATIC when a non-HT association is used just for more use. It could be
anything really, or even an invalid value. Also remember that SMPS_OFF
means all chains turned on.
> > --- wireless-testing.orig/net/mac80211/util.c 2009-11-29 11:33:00.000000000 +0100
> > +++ wireless-testing/net/mac80211/util.c 2009-11-29 11:35:06.000000000 +0100
> > @@ -1170,3 +1170,77 @@ int ieee80211_reconfig(struct ieee80211_
> > return 0;
> > }
> >
> > +static int check_mgd_smps(struct ieee80211_if_managed *ifmgd,
> > + enum nl80211_smps_mode *smps_mode)
> > +{
> > + if (ifmgd->associated) {
> > + *smps_mode = ifmgd->ap_smps;
> > +
> > + if (smps_mode == NL80211_SMPS_AUTOMATIC) {
>
> This should be *smps_mode.
Indeed, good catch, thanks!
johannes
[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 801 bytes --]
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH 2/2 v2] mac80211: enable spatial multiplexing powersave
2009-11-30 15:38 ` [PATCH 2/2] mac80211: enable spatial multiplexing powersave Johannes Berg
2009-11-30 16:54 ` Luis R. Rodriguez
@ 2009-11-30 17:15 ` Johannes Berg
1 sibling, 0 replies; 12+ messages in thread
From: Johannes Berg @ 2009-11-30 17:15 UTC (permalink / raw)
To: John Linville; +Cc: linux-wireless
Enable spatial multiplexing in mac80211 by telling the
driver what to do and, where necessary, sending action
frames to the AP to update the requested SMPS mode.
Also includes a trivial implementation for hwsim that
just logs the requested mode.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
---
v2: (thanks to Luis)
* fix typo "sasociation"
* rework locking in cfg hooks
* add missing dereference in reconfig check
drivers/net/wireless/mac80211_hwsim.c | 22 +++++++-
include/linux/ieee80211.h | 25 +++++++++-
include/net/mac80211.h | 44 +++++++++++++++++
net/mac80211/cfg.c | 85 ++++++++++++++++++++++++++++++++++
net/mac80211/driver-trace.h | 2
net/mac80211/ht.c | 47 ++++++++++++++++++
net/mac80211/ieee80211_i.h | 12 ++++
net/mac80211/main.c | 24 +++++++++
net/mac80211/mlme.c | 63 +++++++++++++++++++++++--
net/mac80211/status.c | 38 +++++++++++++++
net/mac80211/util.c | 74 +++++++++++++++++++++++++++++
11 files changed, 428 insertions(+), 8 deletions(-)
--- wireless-testing.orig/include/net/mac80211.h 2009-11-29 11:33:52.000000000 +0100
+++ wireless-testing/include/net/mac80211.h 2009-11-29 11:35:06.000000000 +0100
@@ -597,8 +597,10 @@ enum ieee80211_conf_flags {
* @IEEE80211_CONF_CHANGE_CHANNEL: the channel/channel_type changed
* @IEEE80211_CONF_CHANGE_RETRY_LIMITS: retry limits changed
* @IEEE80211_CONF_CHANGE_IDLE: Idle flag changed
+ * @IEEE80211_CONF_CHANGE_SMPS: Spatial multiplexing powersave mode changed
*/
enum ieee80211_conf_changed {
+ IEEE80211_CONF_CHANGE_SMPS = BIT(1),
IEEE80211_CONF_CHANGE_LISTEN_INTERVAL = BIT(2),
IEEE80211_CONF_CHANGE_MONITOR = BIT(3),
IEEE80211_CONF_CHANGE_PS = BIT(4),
@@ -636,6 +638,10 @@ enum ieee80211_conf_changed {
* @short_frame_max_tx_count: Maximum number of transmissions for a "short"
* frame, called "dot11ShortRetryLimit" in 802.11, but actually means the
* number of transmissions not the number of retries
+ *
+ * @smps_mode: spatial multiplexing powersave mode; note that
+ * %NL80211_SMPS_STATIC is used when the device is not
+ * configured for an HT channel
*/
struct ieee80211_conf {
u32 flags;
@@ -648,6 +654,7 @@ struct ieee80211_conf {
struct ieee80211_channel *channel;
enum nl80211_channel_type channel_type;
+ enum nl80211_smps_mode smps_mode;
};
/**
@@ -930,6 +937,16 @@ enum ieee80211_tkip_key_type {
* @IEEE80211_HW_BEACON_FILTER:
* Hardware supports dropping of irrelevant beacon frames to
* avoid waking up cpu.
+ *
+ * @IEEE80211_HW_SUPPORTS_STATIC_SMPS:
+ * Hardware supports static spatial multiplexing powersave,
+ * ie. can turn off all but one chain even on HT connections
+ * that should be using more chains.
+ *
+ * @IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS:
+ * Hardware supports dynamic spatial multiplexing powersave,
+ * ie. can turn off all but one chain and then wake the rest
+ * up as required after, for example, rts/cts handshake.
*/
enum ieee80211_hw_flags {
IEEE80211_HW_HAS_RATE_CONTROL = 1<<0,
@@ -947,6 +964,8 @@ enum ieee80211_hw_flags {
IEEE80211_HW_SUPPORTS_DYNAMIC_PS = 1<<12,
IEEE80211_HW_MFP_CAPABLE = 1<<13,
IEEE80211_HW_BEACON_FILTER = 1<<14,
+ IEEE80211_HW_SUPPORTS_STATIC_SMPS = 1<<15,
+ IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS = 1<<16,
};
/**
@@ -1215,6 +1234,31 @@ ieee80211_get_alt_retry_rate(const struc
*/
/**
+ * DOC: Spatial multiplexing power save
+ *
+ * SMPS (Spatial multiplexing power save) is a mechanism to conserve
+ * power in an 802.11n implementation. For details on the mechanism
+ * and rationale, please refer to 802.11 (as amended by 802.11n-2009)
+ * "11.2.3 SM power save".
+ *
+ * The mac80211 implementation is capable of sending action frames
+ * to update the AP about the station's SMPS mode, and will instruct
+ * the driver to enter the specific mode. It will also announce the
+ * requested SMPS mode during the association handshake. Hardware
+ * support for this feature is required, and can be indicated by
+ * hardware flags.
+ *
+ * The default mode will be "automatic", which nl80211/cfg80211
+ * defines to be dynamic SMPS in (regular) powersave, and SMPS
+ * turned off otherwise.
+ *
+ * To support this feature, the driver must set the appropriate
+ * hardware support flags, and handle the SMPS flag to the config()
+ * operation. It will then with this mechanism be instructed to
+ * enter the requested SMPS mode while associated to an HT AP.
+ */
+
+/**
* DOC: Frame filtering
*
* mac80211 requires to see many management frames for proper
--- wireless-testing.orig/net/mac80211/ieee80211_i.h 2009-11-29 11:33:29.000000000 +0100
+++ wireless-testing/net/mac80211/ieee80211_i.h 2009-11-29 22:18:08.000000000 +0100
@@ -297,6 +297,8 @@ struct ieee80211_if_managed {
unsigned long timers_running; /* used for quiesce/restart */
bool powersave; /* powersave requested for this iface */
+ enum nl80211_smps_mode req_smps, /* requested smps mode */
+ ap_smps; /* smps mode AP thinks we're in */
unsigned long request;
@@ -587,6 +589,9 @@ struct ieee80211_local {
/* used for uploading changed mc list */
struct work_struct reconfig_filter;
+ /* used to reconfigure hardware SM PS */
+ struct work_struct recalc_smps;
+
/* aggregated multicast list */
struct dev_addr_list *mc_list;
int mc_count;
@@ -760,6 +765,8 @@ struct ieee80211_local {
int user_power_level; /* in dBm */
int power_constr_level; /* in dBm */
+ enum nl80211_smps_mode smps_mode;
+
struct work_struct restart_work;
#ifdef CONFIG_MAC80211_DEBUGFS
@@ -983,6 +990,9 @@ void ieee80211_send_bar(struct ieee80211
void ieee80211_send_delba(struct ieee80211_sub_if_data *sdata,
const u8 *da, u16 tid,
u16 initiator, u16 reason_code);
+int ieee80211_send_smps_action(struct ieee80211_sub_if_data *sdata,
+ enum nl80211_smps_mode smps, const u8 *da,
+ const u8 *bssid);
void ieee80211_sta_stop_rx_ba_session(struct ieee80211_sub_if_data *sdata, u8 *da,
u16 tid, u16 initiator, u16 reason);
@@ -1093,6 +1103,8 @@ void ieee80211_sta_def_wmm_params(struct
u32 ieee80211_sta_get_rates(struct ieee80211_local *local,
struct ieee802_11_elems *elems,
enum ieee80211_band band);
+void ieee80211_recalc_smps(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *forsdata);
#ifdef CONFIG_MAC80211_NOINLINE
#define debug_noinline noinline
--- wireless-testing.orig/net/mac80211/cfg.c 2009-11-29 11:33:00.000000000 +0100
+++ wireless-testing/net/mac80211/cfg.c 2009-11-30 18:06:44.000000000 +0100
@@ -1316,6 +1316,62 @@ static int ieee80211_testmode_cmd(struct
}
#endif
+static int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata,
+ enum nl80211_smps_mode smps_mode)
+{
+ const u8 *ap;
+ enum nl80211_smps_mode old_req;
+ int err;
+
+ old_req = sdata->u.mgd.req_smps;
+ sdata->u.mgd.req_smps = smps_mode;
+
+ if (old_req == smps_mode &&
+ smps_mode != NL80211_SMPS_AUTOMATIC)
+ return 0;
+
+ /*
+ * If not associated, or current association is not an HT
+ * association, there's no need to send an action frame.
+ */
+ if (!sdata->u.mgd.associated ||
+ sdata->local->oper_channel_type == NL80211_CHAN_NO_HT) {
+ mutex_lock(&sdata->local->iflist_mtx);
+ ieee80211_recalc_smps(sdata->local, sdata);
+ mutex_unlock(&sdata->local->iflist_mtx);
+ return 0;
+ }
+
+ ap = sdata->u.mgd.associated->cbss.bssid;
+
+ if (smps_mode == NL80211_SMPS_AUTOMATIC) {
+ if (sdata->u.mgd.powersave)
+ smps_mode = NL80211_SMPS_DYNAMIC;
+ else
+ smps_mode = NL80211_SMPS_OFF;
+ }
+
+ /* send SM PS frame to AP */
+ err = ieee80211_send_smps_action(sdata, smps_mode,
+ ap, ap);
+ if (err)
+ sdata->u.mgd.req_smps = old_req;
+
+ return err;
+}
+
+static int ieee80211_request_smps(struct ieee80211_sub_if_data *sdata,
+ enum nl80211_smps_mode smps_mode)
+{
+ int err;
+
+ mutex_lock(&sdata->u.mgd.mtx);
+ err = __ieee80211_request_smps(sdata, smps_mode);
+ mutex_unlock(&sdata->u.mgd.mtx);
+
+ return err;
+}
+
static int ieee80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev,
bool enabled, int timeout)
{
@@ -1333,6 +1389,11 @@ static int ieee80211_set_power_mgmt(stru
sdata->u.mgd.powersave = enabled;
conf->dynamic_ps_timeout = timeout;
+ /* no change, but if automatic follow powersave */
+ mutex_lock(&sdata->u.mgd.mtx);
+ __ieee80211_request_smps(sdata, sdata->u.mgd.req_smps);
+ mutex_unlock(&sdata->u.mgd.mtx);
+
if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS)
ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
@@ -1383,6 +1444,29 @@ static int ieee80211_set_bitrate_mask(st
return err;
}
+static int ieee80211_set_smps(struct wiphy *wiphy, struct net_device *dev,
+ enum nl80211_smps_mode smps_mode)
+{
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct ieee80211_local *local = sdata->local;
+
+ if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_STATIC_SMPS) &&
+ smps_mode == NL80211_SMPS_STATIC)
+ return -EINVAL;
+
+ /* auto should be dynamic if in PS mode */
+ if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS) &&
+ (smps_mode == NL80211_SMPS_DYNAMIC ||
+ smps_mode == NL80211_SMPS_AUTOMATIC))
+ return -EINVAL;
+
+ /* supported only on managed interfaces for now */
+ if (sdata->vif.type != NL80211_IFTYPE_STATION)
+ return -EOPNOTSUPP;
+
+ return ieee80211_request_smps(sdata, smps_mode);
+}
+
struct cfg80211_ops mac80211_config_ops = {
.add_virtual_intf = ieee80211_add_iface,
.del_virtual_intf = ieee80211_del_iface,
@@ -1429,4 +1513,5 @@ struct cfg80211_ops mac80211_config_ops
CFG80211_TESTMODE_CMD(ieee80211_testmode_cmd)
.set_power_mgmt = ieee80211_set_power_mgmt,
.set_bitrate_mask = ieee80211_set_bitrate_mask,
+ .set_smps = ieee80211_set_smps,
};
--- wireless-testing.orig/include/linux/ieee80211.h 2009-11-29 11:33:00.000000000 +0100
+++ wireless-testing/include/linux/ieee80211.h 2009-11-29 11:35:06.000000000 +0100
@@ -707,6 +707,10 @@ struct ieee80211_mgmt {
u8 action;
u8 trans_id[WLAN_SA_QUERY_TR_ID_LEN];
} __attribute__ ((packed)) sa_query;
+ struct {
+ u8 action;
+ u8 smps_control;
+ } __attribute__ ((packed)) ht_smps;
} u;
} __attribute__ ((packed)) action;
} u;
@@ -824,6 +828,7 @@ struct ieee80211_ht_cap {
#define IEEE80211_HT_CAP_LDPC_CODING 0x0001
#define IEEE80211_HT_CAP_SUP_WIDTH_20_40 0x0002
#define IEEE80211_HT_CAP_SM_PS 0x000C
+#define IEEE80211_HT_CAP_SM_PS_SHIFT 2
#define IEEE80211_HT_CAP_GRN_FLD 0x0010
#define IEEE80211_HT_CAP_SGI_20 0x0020
#define IEEE80211_HT_CAP_SGI_40 0x0040
@@ -839,6 +844,7 @@ struct ieee80211_ht_cap {
/* 802.11n HT capability AMPDU settings (for ampdu_params_info) */
#define IEEE80211_HT_AMPDU_PARM_FACTOR 0x03
#define IEEE80211_HT_AMPDU_PARM_DENSITY 0x1C
+#define IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT 2
/*
* Maximum length of AMPDU that the STA can receive.
@@ -922,12 +928,17 @@ struct ieee80211_ht_info {
#define IEEE80211_MAX_AMPDU_BUF 0x40
-/* Spatial Multiplexing Power Save Modes */
+/* Spatial Multiplexing Power Save Modes (for capability) */
#define WLAN_HT_CAP_SM_PS_STATIC 0
#define WLAN_HT_CAP_SM_PS_DYNAMIC 1
#define WLAN_HT_CAP_SM_PS_INVALID 2
#define WLAN_HT_CAP_SM_PS_DISABLED 3
+/* for SM power control field lower two bits */
+#define WLAN_HT_SMPS_CONTROL_DISABLED 0
+#define WLAN_HT_SMPS_CONTROL_STATIC 1
+#define WLAN_HT_SMPS_CONTROL_DYNAMIC 3
+
/* Authentication algorithms */
#define WLAN_AUTH_OPEN 0
#define WLAN_AUTH_SHARED_KEY 1
@@ -1150,6 +1161,18 @@ enum ieee80211_spectrum_mgmt_actioncode
WLAN_ACTION_SPCT_CHL_SWITCH = 4,
};
+/* HT action codes */
+enum ieee80211_ht_actioncode {
+ WLAN_HT_ACTION_NOTIFY_CHANWIDTH = 0,
+ WLAN_HT_ACTION_SMPS = 1,
+ WLAN_HT_ACTION_PSMP = 2,
+ WLAN_HT_ACTION_PCO_PHASE = 3,
+ WLAN_HT_ACTION_CSI = 4,
+ WLAN_HT_ACTION_NONCOMPRESSED_BF = 5,
+ WLAN_HT_ACTION_COMPRESSED_BF = 6,
+ WLAN_HT_ACTION_ASEL_IDX_FEEDBACK = 7,
+};
+
/* Security key length */
enum ieee80211_key_len {
WLAN_KEY_LEN_WEP40 = 5,
--- wireless-testing.orig/net/mac80211/mlme.c 2009-11-29 11:33:47.000000000 +0100
+++ wireless-testing/net/mac80211/mlme.c 2009-11-29 22:18:09.000000000 +0100
@@ -398,6 +398,8 @@ static void ieee80211_send_assoc(struct
__le16 tmp;
u32 flags = local->hw.conf.channel->flags;
+ /* determine capability flags */
+
switch (ht_info->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
if (flags & IEEE80211_CHAN_NO_HT40PLUS) {
@@ -413,17 +415,64 @@ static void ieee80211_send_assoc(struct
break;
}
- tmp = cpu_to_le16(cap);
- pos = skb_put(skb, sizeof(struct ieee80211_ht_cap)+2);
+ /* set SM PS mode properly */
+ cap &= ~IEEE80211_HT_CAP_SM_PS;
+ /* new association always uses requested smps mode */
+ if (ifmgd->req_smps == NL80211_SMPS_AUTOMATIC) {
+ if (ifmgd->powersave)
+ ifmgd->ap_smps = NL80211_SMPS_DYNAMIC;
+ else
+ ifmgd->ap_smps = NL80211_SMPS_OFF;
+ } else
+ ifmgd->ap_smps = ifmgd->req_smps;
+
+ switch (ifmgd->ap_smps) {
+ case NL80211_SMPS_AUTOMATIC:
+ case __NL80211_SMPS_NUM:
+ WARN_ON(1);
+ case NL80211_SMPS_OFF:
+ cap |= WLAN_HT_CAP_SM_PS_DISABLED <<
+ IEEE80211_HT_CAP_SM_PS_SHIFT;
+ break;
+ case NL80211_SMPS_STATIC:
+ cap |= WLAN_HT_CAP_SM_PS_STATIC <<
+ IEEE80211_HT_CAP_SM_PS_SHIFT;
+ break;
+ case NL80211_SMPS_DYNAMIC:
+ cap |= WLAN_HT_CAP_SM_PS_DYNAMIC <<
+ IEEE80211_HT_CAP_SM_PS_SHIFT;
+ break;
+ }
+
+ /* reserve and fill IE */
+
+ pos = skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2);
*pos++ = WLAN_EID_HT_CAPABILITY;
*pos++ = sizeof(struct ieee80211_ht_cap);
memset(pos, 0, sizeof(struct ieee80211_ht_cap));
+
+ /* capability flags */
+ tmp = cpu_to_le16(cap);
memcpy(pos, &tmp, sizeof(u16));
pos += sizeof(u16);
- /* TODO: needs a define here for << 2 */
+
+ /* AMPDU parameters */
*pos++ = sband->ht_cap.ampdu_factor |
- (sband->ht_cap.ampdu_density << 2);
+ (sband->ht_cap.ampdu_density <<
+ IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT);
+
+ /* MCS set */
memcpy(pos, &sband->ht_cap.mcs, sizeof(sband->ht_cap.mcs));
+ pos += sizeof(sband->ht_cap.mcs);
+
+ /* extended capabilities */
+ pos += sizeof(__le16);
+
+ /* BF capabilities */
+ pos += sizeof(__le32);
+
+ /* antenna selection */
+ pos += sizeof(u8);
}
IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
@@ -932,6 +981,7 @@ static void ieee80211_set_associated(str
mutex_lock(&local->iflist_mtx);
ieee80211_recalc_ps(local, -1);
+ ieee80211_recalc_smps(local, sdata);
mutex_unlock(&local->iflist_mtx);
netif_start_queue(sdata->dev);
@@ -2328,6 +2378,11 @@ void ieee80211_sta_setup_sdata(struct ie
ifmgd->flags |= IEEE80211_STA_WMM_ENABLED;
mutex_init(&ifmgd->mtx);
+
+ if (sdata->local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS)
+ ifmgd->req_smps = NL80211_SMPS_AUTOMATIC;
+ else
+ ifmgd->req_smps = NL80211_SMPS_OFF;
}
/* scan finished notification */
--- wireless-testing.orig/net/mac80211/ht.c 2009-11-29 11:33:29.000000000 +0100
+++ wireless-testing/net/mac80211/ht.c 2009-11-29 11:35:06.000000000 +0100
@@ -166,3 +166,50 @@ void ieee80211_process_delba(struct ieee
spin_unlock_bh(&sta->lock);
}
}
+
+int ieee80211_send_smps_action(struct ieee80211_sub_if_data *sdata,
+ enum nl80211_smps_mode smps, const u8 *da,
+ const u8 *bssid)
+{
+ struct ieee80211_local *local = sdata->local;
+ struct sk_buff *skb;
+ struct ieee80211_mgmt *action_frame;
+
+ /* 27 = header + category + action + smps mode */
+ skb = dev_alloc_skb(27 + local->hw.extra_tx_headroom);
+ if (!skb)
+ return -ENOMEM;
+
+ skb_reserve(skb, local->hw.extra_tx_headroom);
+ action_frame = (void *)skb_put(skb, 27);
+ memcpy(action_frame->da, da, ETH_ALEN);
+ memcpy(action_frame->sa, sdata->dev->dev_addr, ETH_ALEN);
+ memcpy(action_frame->bssid, bssid, ETH_ALEN);
+ action_frame->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
+ IEEE80211_STYPE_ACTION);
+ action_frame->u.action.category = WLAN_CATEGORY_HT;
+ action_frame->u.action.u.ht_smps.action = WLAN_HT_ACTION_SMPS;
+ switch (smps) {
+ case NL80211_SMPS_AUTOMATIC:
+ case __NL80211_SMPS_NUM:
+ WARN_ON(1);
+ case NL80211_SMPS_OFF:
+ action_frame->u.action.u.ht_smps.smps_control =
+ WLAN_HT_SMPS_CONTROL_DISABLED;
+ break;
+ case NL80211_SMPS_STATIC:
+ action_frame->u.action.u.ht_smps.smps_control =
+ WLAN_HT_SMPS_CONTROL_STATIC;
+ break;
+ case NL80211_SMPS_DYNAMIC:
+ action_frame->u.action.u.ht_smps.smps_control =
+ WLAN_HT_SMPS_CONTROL_DYNAMIC;
+ break;
+ }
+
+ /* we'll do more on status of this frame */
+ IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS;
+ ieee80211_tx_skb(sdata, skb);
+
+ return 0;
+}
--- wireless-testing.orig/net/mac80211/status.c 2009-11-29 11:33:00.000000000 +0100
+++ wireless-testing/net/mac80211/status.c 2009-11-29 11:35:06.000000000 +0100
@@ -134,6 +134,40 @@ static void ieee80211_handle_filtered_fr
dev_kfree_skb(skb);
}
+static void ieee80211_frame_acked(struct sta_info *sta, struct sk_buff *skb)
+{
+ struct ieee80211_mgmt *mgmt = (void *) skb->data;
+ struct ieee80211_local *local = sta->local;
+ struct ieee80211_sub_if_data *sdata = sta->sdata;
+
+ if (ieee80211_is_action(mgmt->frame_control) &&
+ sdata->vif.type == NL80211_IFTYPE_STATION &&
+ mgmt->u.action.category == WLAN_CATEGORY_HT &&
+ mgmt->u.action.u.ht_smps.action == WLAN_HT_ACTION_SMPS) {
+ /*
+ * This update looks racy, but isn't -- if we come
+ * here we've definitely got a station that we're
+ * talking to, and on a managed interface that can
+ * only be the AP. And the only other place updating
+ * this variable is before we're associated.
+ */
+ switch (mgmt->u.action.u.ht_smps.smps_control) {
+ case WLAN_HT_SMPS_CONTROL_DYNAMIC:
+ sta->sdata->u.mgd.ap_smps = NL80211_SMPS_DYNAMIC;
+ break;
+ case WLAN_HT_SMPS_CONTROL_STATIC:
+ sta->sdata->u.mgd.ap_smps = NL80211_SMPS_STATIC;
+ break;
+ case WLAN_HT_SMPS_CONTROL_DISABLED:
+ default: /* shouldn't happen since we don't send that */
+ sta->sdata->u.mgd.ap_smps = NL80211_SMPS_OFF;
+ break;
+ }
+
+ ieee80211_queue_work(&local->hw, &local->recalc_smps);
+ }
+}
+
void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
{
struct sk_buff *skb2;
@@ -209,6 +243,10 @@ void ieee80211_tx_status(struct ieee8021
rate_control_tx_status(local, sband, sta, skb);
if (ieee80211_vif_is_mesh(&sta->sdata->vif))
ieee80211s_update_metric(local, sta, skb);
+
+ if (!(info->flags & IEEE80211_TX_CTL_INJECTED) &&
+ (info->flags & IEEE80211_TX_STAT_ACK))
+ ieee80211_frame_acked(sta, skb);
}
rcu_read_unlock();
--- wireless-testing.orig/net/mac80211/main.c 2009-11-29 11:33:00.000000000 +0100
+++ wireless-testing/net/mac80211/main.c 2009-11-29 22:18:07.000000000 +0100
@@ -113,6 +113,18 @@ int ieee80211_hw_config(struct ieee80211
changed |= IEEE80211_CONF_CHANGE_CHANNEL;
}
+ if (!conf_is_ht(&local->hw.conf)) {
+ /*
+ * mac80211.h documents that this is only valid
+ * when the channel is set to an HT type, and
+ * that otherwise STATIC is used.
+ */
+ local->hw.conf.smps_mode = NL80211_SMPS_STATIC;
+ } else if (local->hw.conf.smps_mode != local->smps_mode) {
+ local->hw.conf.smps_mode = local->smps_mode;
+ changed |= IEEE80211_CONF_CHANGE_SMPS;
+ }
+
if (scan_chan)
power = chan->max_power;
else
@@ -297,6 +309,16 @@ void ieee80211_restart_hw(struct ieee802
}
EXPORT_SYMBOL(ieee80211_restart_hw);
+static void ieee80211_recalc_smps_work(struct work_struct *work)
+{
+ struct ieee80211_local *local =
+ container_of(work, struct ieee80211_local, recalc_smps);
+
+ mutex_lock(&local->iflist_mtx);
+ ieee80211_recalc_smps(local, NULL);
+ mutex_unlock(&local->iflist_mtx);
+}
+
struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
const struct ieee80211_ops *ops)
{
@@ -370,6 +392,8 @@ struct ieee80211_hw *ieee80211_alloc_hw(
INIT_WORK(&local->restart_work, ieee80211_restart_work);
INIT_WORK(&local->reconfig_filter, ieee80211_reconfig_filter);
+ INIT_WORK(&local->recalc_smps, ieee80211_recalc_smps_work);
+ local->smps_mode = NL80211_SMPS_OFF;
INIT_WORK(&local->dynamic_ps_enable_work,
ieee80211_dynamic_ps_enable_work);
--- wireless-testing.orig/net/mac80211/util.c 2009-11-29 11:33:00.000000000 +0100
+++ wireless-testing/net/mac80211/util.c 2009-11-30 18:07:13.000000000 +0100
@@ -1170,3 +1170,77 @@ int ieee80211_reconfig(struct ieee80211_
return 0;
}
+static int check_mgd_smps(struct ieee80211_if_managed *ifmgd,
+ enum nl80211_smps_mode *smps_mode)
+{
+ if (ifmgd->associated) {
+ *smps_mode = ifmgd->ap_smps;
+
+ if (*smps_mode == NL80211_SMPS_AUTOMATIC) {
+ if (ifmgd->powersave)
+ *smps_mode = NL80211_SMPS_DYNAMIC;
+ else
+ *smps_mode = NL80211_SMPS_OFF;
+ }
+
+ return 1;
+ }
+
+ return 0;
+}
+
+/* must hold iflist_mtx */
+void ieee80211_recalc_smps(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *forsdata)
+{
+ struct ieee80211_sub_if_data *sdata;
+ enum nl80211_smps_mode smps_mode = NL80211_SMPS_OFF;
+ int count = 0;
+
+ if (forsdata)
+ WARN_ON(!mutex_is_locked(&forsdata->u.mgd.mtx));
+
+ WARN_ON(!mutex_is_locked(&local->iflist_mtx));
+
+ /*
+ * This function could be improved to handle multiple
+ * interfaces better, but right now it makes any
+ * non-station interfaces force SM PS to be turned
+ * off. If there are multiple station interfaces it
+ * could also use the best possible mode, e.g. if
+ * one is in static and the other in dynamic then
+ * dynamic is ok.
+ */
+
+ list_for_each_entry(sdata, &local->interfaces, list) {
+ if (!netif_running(sdata->dev))
+ continue;
+ if (sdata->vif.type != NL80211_IFTYPE_STATION)
+ goto set;
+ if (sdata != forsdata) {
+ /*
+ * This nested is ok -- we are holding the iflist_mtx
+ * so can't get here twice or so. But it's required
+ * since normally we acquire it first and then the
+ * iflist_mtx.
+ */
+ mutex_lock_nested(&sdata->u.mgd.mtx, SINGLE_DEPTH_NESTING);
+ count += check_mgd_smps(&sdata->u.mgd, &smps_mode);
+ mutex_unlock(&sdata->u.mgd.mtx);
+ } else
+ count += check_mgd_smps(&sdata->u.mgd, &smps_mode);
+
+ if (count > 1) {
+ smps_mode = NL80211_SMPS_OFF;
+ break;
+ }
+ }
+
+ if (smps_mode == local->smps_mode)
+ return;
+
+ set:
+ local->smps_mode = smps_mode;
+ /* changed flag is auto-detected for this */
+ ieee80211_hw_config(local, 0);
+}
--- wireless-testing.orig/drivers/net/wireless/mac80211_hwsim.c 2009-11-29 11:33:52.000000000 +0100
+++ wireless-testing/drivers/net/wireless/mac80211_hwsim.c 2009-11-29 11:35:06.000000000 +0100
@@ -619,12 +619,26 @@ static int mac80211_hwsim_config(struct
{
struct mac80211_hwsim_data *data = hw->priv;
struct ieee80211_conf *conf = &hw->conf;
+ static const char *chantypes[4] = {
+ [NL80211_CHAN_NO_HT] = "noht",
+ [NL80211_CHAN_HT20] = "ht20",
+ [NL80211_CHAN_HT40MINUS] = "ht40-",
+ [NL80211_CHAN_HT40PLUS] = "ht40+",
+ };
+ static const char *smps_modes[__NL80211_SMPS_NUM] = {
+ [NL80211_SMPS_AUTOMATIC] = "auto/invalid",
+ [NL80211_SMPS_OFF] = "off",
+ [NL80211_SMPS_STATIC] = "static",
+ [NL80211_SMPS_DYNAMIC] = "dynamic",
+ };
- printk(KERN_DEBUG "%s:%s (freq=%d idle=%d ps=%d)\n",
+ printk(KERN_DEBUG "%s:%s (freq=%d/%s idle=%d ps=%d smps=%s)\n",
wiphy_name(hw->wiphy), __func__,
conf->channel->center_freq,
+ chantypes[conf->channel_type],
!!(conf->flags & IEEE80211_CONF_IDLE),
- !!(conf->flags & IEEE80211_CONF_PS));
+ !!(conf->flags & IEEE80211_CONF_PS),
+ smps_modes[conf->smps_mode]);
data->idle = !!(conf->flags & IEEE80211_CONF_IDLE);
@@ -1121,7 +1135,9 @@ static int __init init_mac80211_hwsim(vo
BIT(NL80211_IFTYPE_MESH_POINT);
hw->flags = IEEE80211_HW_MFP_CAPABLE |
- IEEE80211_HW_SIGNAL_DBM;
+ IEEE80211_HW_SIGNAL_DBM |
+ IEEE80211_HW_SUPPORTS_STATIC_SMPS |
+ IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS;
/* ask mac80211 to reserve space for magic */
hw->vif_data_size = sizeof(struct hwsim_vif_priv);
--- wireless-testing.orig/net/mac80211/driver-trace.h 2009-11-29 22:18:24.000000000 +0100
+++ wireless-testing/net/mac80211/driver-trace.h 2009-11-29 22:19:04.000000000 +0100
@@ -140,6 +140,7 @@ TRACE_EVENT(drv_config,
__field(u8, short_frame_max_tx_count)
__field(int, center_freq)
__field(int, channel_type)
+ __field(int, smps)
),
TP_fast_assign(
@@ -155,6 +156,7 @@ TRACE_EVENT(drv_config,
__entry->short_frame_max_tx_count = local->hw.conf.short_frame_max_tx_count;
__entry->center_freq = local->hw.conf.channel->center_freq;
__entry->channel_type = local->hw.conf.channel_type;
+ __entry->smps = local->hw.conf.smps_mode;
),
TP_printk(
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 2/2] mac80211: enable spatial multiplexing powersave
2009-11-30 17:04 ` Johannes Berg
@ 2009-11-30 17:22 ` Luis R. Rodriguez
2009-11-30 17:37 ` Johannes Berg
0 siblings, 1 reply; 12+ messages in thread
From: Luis R. Rodriguez @ 2009-11-30 17:22 UTC (permalink / raw)
To: Johannes Berg; +Cc: John Linville, linux-wireless
On Mon, Nov 30, 2009 at 9:04 AM, Johannes Berg
<johannes@sipsolutions.net> wrote:
>
>> > mutex_init(&ifmgd->mtx);
>> > +
>> > + if (sdata->local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS)
>> > + ifmgd->req_smps = NL80211_SMPS_AUTOMATIC;
>> > + else
>> > + ifmgd->req_smps = NL80211_SMPS_OFF;
>>
>> How about just checking the HT cap as well and if no HT cap has been
>> set warn as it would indicate a driver bug actually.
>
> You mean if it sets dynps w/o HT cap?
Yes
> We /could/ do that, but I don't
> really see a reason for it. We'd find out soon enough by a non-HT driver
> supporting SMPS etc.
OK.
>> > + ieee80211_queue_work(&local->hw, &local->recalc_smps);
>>
>> OK from what I gather so far we just queue work if we received an ACK
>> from the action frame we sent. Since we tell the device to
>> ieee80211_stop_device() the device should have stopped RX'ing and that
>> *should* mean preventing processing future tx status reports. We'll
>> find out if that assumption is wrong.
>
> It's not about receiving -- only about transmitting.
Yes but this is not about a actual transmit but a tx status.
> But yeah, we'll
> find out. I have a patch pending that'll flush queues etc., that would
> make it completely impossible if implemented by the driver, but I don't
> think we need to worry about it.
What I mean is that technically a driver could have called even this
new tx flush and yet *after* that was called a tx status for a
*previously* transmitted frame could have come through hardware, which
is why I mentioned hw stop. hw stop would seem to be the only thing
ensuring we don't queue more work if we'd try to pm-suspend between
the point where we enabled smps and before the AP sent an ACK to us
for the action frame.
>> > @@ -209,6 +243,10 @@ void ieee80211_tx_status(struct ieee8021
>> > rate_control_tx_status(local, sband, sta, skb);
>> > if (ieee80211_vif_is_mesh(&sta->sdata->vif))
>> > ieee80211s_update_metric(local, sta, skb);
>> > +
>> > + if (!(info->flags & IEEE80211_TX_CTL_INJECTED) &&
>> > + (info->flags & IEEE80211_TX_STAT_ACK))
>> > + ieee80211_frame_acked(sta, skb);
>> > }
>> >
>> > rcu_read_unlock();
>>
>> Nice, should the nullfunc ack check be handled here as well?
>
> Well, it could, in theory. But I've now imposed a requirement that SMPS
> capable devices also implement status notifications properly, which may
> or may not be possible for all frames. In fact, we may need to change
> this in the future but for now I prefer this requirement -- but for HT
> only.
OK I see. Then the next question is can iwl3945 and iwlagn report tx
status for a nullfunc sent to an AP?
>> > + if (!conf_is_ht(&local->hw.conf)) {
>> > + /*
>> > + * mac80211.h documents that this is only valid
>> > + * when the channel is set to an HT type, and
>> > + * that otherwise STATIC is used.
>> > + */
>> > + local->hw.conf.smps_mode = NL80211_SMPS_STATIC;
>>
>> Why wouldn't this just be SMPS_OFF ?
>
> Well, it doesn't matter either way. However, it was easier this way for
> iwlwifi because ultimately we want to turn off all but one chain if
> non-HT and/or static SMPS. So at least there it made sense to use STATIC
> here to avoid the extra check. But other drivers may or may not want to
> ignore it completely in the HT case. I've documented that it sets it to
> STATIC when a non-HT association is used just for more use. It could be
> anything really, or even an invalid value. Also remember that SMPS_OFF
> means all chains turned on.
Hm -- so hardware would *typically* leave all chains on even for
legacy mode of operation?
Luis
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 2/2] mac80211: enable spatial multiplexing powersave
2009-11-30 17:22 ` Luis R. Rodriguez
@ 2009-11-30 17:37 ` Johannes Berg
0 siblings, 0 replies; 12+ messages in thread
From: Johannes Berg @ 2009-11-30 17:37 UTC (permalink / raw)
To: Luis R. Rodriguez; +Cc: John Linville, linux-wireless
[-- Attachment #1: Type: text/plain, Size: 3052 bytes --]
On Mon, 2009-11-30 at 09:22 -0800, Luis R. Rodriguez wrote:
> >> > + ieee80211_queue_work(&local->hw, &local->recalc_smps);
> >>
> >> OK from what I gather so far we just queue work if we received an ACK
> >> from the action frame we sent. Since we tell the device to
> >> ieee80211_stop_device() the device should have stopped RX'ing and that
> >> *should* mean preventing processing future tx status reports. We'll
> >> find out if that assumption is wrong.
> >
> > It's not about receiving -- only about transmitting.
>
> Yes but this is not about a actual transmit but a tx status.
Right.
> > But yeah, we'll
> > find out. I have a patch pending that'll flush queues etc., that would
> > make it completely impossible if implemented by the driver, but I don't
> > think we need to worry about it.
>
> What I mean is that technically a driver could have called even this
> new tx flush and yet *after* that was called a tx status for a
> *previously* transmitted frame could have come through hardware, which
> is why I mentioned hw stop. hw stop would seem to be the only thing
> ensuring we don't queue more work if we'd try to pm-suspend between
> the point where we enabled smps and before the AP sent an ACK to us
> for the action frame.
Yeah, I see what you're saying, I just don't think it'll actually
happen. I guess we will find out by the warning if it ever does.
> OK I see. Then the next question is can iwl3945 and iwlagn report tx
> status for a nullfunc sent to an AP?
It doesn't matter for them -- they do it all in the firmware. hw_scan
and complete PS mode offload.
> > Well, it doesn't matter either way. However, it was easier this way for
> > iwlwifi because ultimately we want to turn off all but one chain if
> > non-HT and/or static SMPS. So at least there it made sense to use STATIC
> > here to avoid the extra check. But other drivers may or may not want to
> > ignore it completely in the HT case. I've documented that it sets it to
> > STATIC when a non-HT association is used just for more use. It could be
> > anything really, or even an invalid value. Also remember that SMPS_OFF
> > means all chains turned on.
>
> Hm -- so hardware would *typically* leave all chains on even for
> legacy mode of operation?
It depends on what you're after. You can leave them all on, and you'll
have better reception. Or you can turn off all but one, and you'll save
power. Better reception doesn't help you _much_ because the AP still
also has to receive your stuff, and if it's a legacy AP it only has one
chain so you receiving it well won't necessarily mean it receives you
well enough for a connection -- we would therefore like to save more
power and use only one chain on a legacy AP. Hence the default here, it
makes it easier the way iwlwifi is done.
I had "Automatic" here as the default in my first iteration of the patch
but it just ended up being a few lines of code more in iwlwifi and no
real difference in mac80211.
johannes
[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 801 bytes --]
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 1/2] cfg80211: add SM PS hooks
2009-11-30 15:38 ` [PATCH 1/2] cfg80211: add SM PS hooks Johannes Berg
@ 2009-12-01 7:01 ` Kalle Valo
2009-12-01 9:29 ` Johannes Berg
0 siblings, 1 reply; 12+ messages in thread
From: Kalle Valo @ 2009-12-01 7:01 UTC (permalink / raw)
To: Johannes Berg; +Cc: John Linville, linux-wireless
Johannes Berg <johannes@sipsolutions.net> writes:
> Add spatial multiplexing power save configuration
> hooks to cfg80211/nl80211.
Is it really necessary to export this to user space, at least via
nl80211? If we add this to nl80211, we have to support this almost
forever.
Also how are planning to use this? What components will use the
interface and how?
My concern here is that we will end up having, yet again, complicated
user space interface for power save. The ideal situation would be that
kernel would configure all this automatically and we would have a
simple interface just to disable power save in cases where it doesn't
work (broken APs etc).
--
Kalle Valo
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 1/2] cfg80211: add SM PS hooks
2009-12-01 7:01 ` Kalle Valo
@ 2009-12-01 9:29 ` Johannes Berg
2009-12-01 11:44 ` Kalle Valo
0 siblings, 1 reply; 12+ messages in thread
From: Johannes Berg @ 2009-12-01 9:29 UTC (permalink / raw)
To: Kalle Valo; +Cc: John Linville, linux-wireless
[-- Attachment #1: Type: text/plain, Size: 1823 bytes --]
On Tue, 2009-12-01 at 09:01 +0200, Kalle Valo wrote:
> Johannes Berg <johannes@sipsolutions.net> writes:
>
> > Add spatial multiplexing power save configuration
> > hooks to cfg80211/nl80211.
>
> Is it really necessary to export this to user space, at least via
> nl80211? If we add this to nl80211, we have to support this almost
> forever.
>
> Also how are planning to use this? What components will use the
> interface and how?
Good questions.
> My concern here is that we will end up having, yet again, complicated
> user space interface for power save. The ideal situation would be that
> kernel would configure all this automatically and we would have a
> simple interface just to disable power save in cases where it doesn't
> work (broken APs etc).
I'm not actually sure that there are APs broken wrt. SM PS, it seems
highly unlikely -- even if there are (like mac80211!!) the rate control
algorithm would recover the situation.
As such, I suppose it's somewhat a debugging API. On the other hand,
there are actual traffic throughput consequences of enabling SM PS, even
when in powersave. Powersave will be turned off automatically if there's
enough traffic, but SM PS won't. The default, automatic, means it just
follows powersave (dynamic SM PS if on, no SM PS if off), but I'm not
sure that is typically desirable.
Of course, on the other hand, I don't see many users actually setting
it. An alternative could be to try to estimate traffic, but that gets
messy fairly quickly because the thresholds depend on the environment.
If you prefer to leave it out for now, I can probably move it to debugfs
until we have a more clearly defined use case, although if we decide
that we never want it then obviously the action frame handling can go
away again.
johannes
[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 801 bytes --]
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 1/2] cfg80211: add SM PS hooks
2009-12-01 9:29 ` Johannes Berg
@ 2009-12-01 11:44 ` Kalle Valo
0 siblings, 0 replies; 12+ messages in thread
From: Kalle Valo @ 2009-12-01 11:44 UTC (permalink / raw)
To: Johannes Berg; +Cc: John Linville, linux-wireless
Johannes Berg <johannes@sipsolutions.net> writes:
> On Tue, 2009-12-01 at 09:01 +0200, Kalle Valo wrote:
>> My concern here is that we will end up having, yet again, complicated
>> user space interface for power save. The ideal situation would be that
>> kernel would configure all this automatically and we would have a
>> simple interface just to disable power save in cases where it doesn't
>> work (broken APs etc).
>
> I'm not actually sure that there are APs broken wrt. SM PS, it seems
> highly unlikely -- even if there are (like mac80211!!) the rate control
> algorithm would recover the situation.
Oh, that's good.
> As such, I suppose it's somewhat a debugging API. On the other hand,
> there are actual traffic throughput consequences of enabling SM PS, even
> when in powersave. Powersave will be turned off automatically if there's
> enough traffic, but SM PS won't. The default, automatic, means it just
> follows powersave (dynamic SM PS if on, no SM PS if off), but I'm not
> sure that is typically desirable.
>
> Of course, on the other hand, I don't see many users actually setting
> it. An alternative could be to try to estimate traffic, but that gets
> messy fairly quickly because the thresholds depend on the environment.
I think at some point we need to start estimating the traffic somehow
and having heuristics to dynamically change the power save settings.
But it's a huge challenge, so it isn't coming anytime soon.
> If you prefer to leave it out for now, I can probably move it to debugfs
> until we have a more clearly defined use case,
Great if you can convert it to debugfs for now. That way we have a bit
more time to think about the power save interface. Thanks.
> although if we decide that we never want it then obviously the
> action frame handling can go away again.
Yeah.
--
Kalle Valo
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v3] mac80211: enable spatial multiplexing powersave
2009-11-30 15:38 [PATCH 0/2] Spatial multiplexing powersave (client support) Johannes Berg
2009-11-30 15:38 ` [PATCH 1/2] cfg80211: add SM PS hooks Johannes Berg
2009-11-30 15:38 ` [PATCH 2/2] mac80211: enable spatial multiplexing powersave Johannes Berg
@ 2009-12-01 12:37 ` Johannes Berg
2 siblings, 0 replies; 12+ messages in thread
From: Johannes Berg @ 2009-12-01 12:37 UTC (permalink / raw)
To: John Linville; +Cc: linux-wireless, Kalle Valo
Enable spatial multiplexing in mac80211 by telling the
driver what to do and, where necessary, sending action
frames to the AP to update the requested SMPS mode.
Also includes a trivial implementation for hwsim that
just logs the requested mode.
For now, the userspace interface is in debugfs only,
and let you toggle the requested mode at any time.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
---
v2: (thanks to Luis)
* fix typo "sasociation"
* rework locking in cfg hooks
* add missing dereference in reconfig check
v3:
* use debugfs as requested by Kalle, replaces
both previous patches then
drivers/net/wireless/mac80211_hwsim.c | 22 +++++-
include/linux/ieee80211.h | 25 +++++++
include/net/mac80211.h | 59 ++++++++++++++++++
net/mac80211/cfg.c | 49 +++++++++++++++
net/mac80211/debugfs_netdev.c | 111 +++++++++++++++++++++++++++++++++-
net/mac80211/driver-trace.h | 2
net/mac80211/ht.c | 47 ++++++++++++++
net/mac80211/ieee80211_i.h | 14 ++++
net/mac80211/main.c | 24 +++++++
net/mac80211/mlme.c | 63 ++++++++++++++++++-
net/mac80211/status.c | 38 +++++++++++
net/mac80211/util.c | 74 ++++++++++++++++++++++
12 files changed, 517 insertions(+), 11 deletions(-)
--- wireless-testing.orig/include/net/mac80211.h 2009-12-01 13:16:12.000000000 +0100
+++ wireless-testing/include/net/mac80211.h 2009-12-01 13:20:33.000000000 +0100
@@ -597,8 +597,10 @@ enum ieee80211_conf_flags {
* @IEEE80211_CONF_CHANGE_CHANNEL: the channel/channel_type changed
* @IEEE80211_CONF_CHANGE_RETRY_LIMITS: retry limits changed
* @IEEE80211_CONF_CHANGE_IDLE: Idle flag changed
+ * @IEEE80211_CONF_CHANGE_SMPS: Spatial multiplexing powersave mode changed
*/
enum ieee80211_conf_changed {
+ IEEE80211_CONF_CHANGE_SMPS = BIT(1),
IEEE80211_CONF_CHANGE_LISTEN_INTERVAL = BIT(2),
IEEE80211_CONF_CHANGE_MONITOR = BIT(3),
IEEE80211_CONF_CHANGE_PS = BIT(4),
@@ -609,6 +611,21 @@ enum ieee80211_conf_changed {
};
/**
+ * enum ieee80211_smps_mode - spatial multiplexing power save mode
+ *
+ * @
+ */
+enum ieee80211_smps_mode {
+ IEEE80211_SMPS_AUTOMATIC,
+ IEEE80211_SMPS_OFF,
+ IEEE80211_SMPS_STATIC,
+ IEEE80211_SMPS_DYNAMIC,
+
+ /* keep last */
+ IEEE80211_SMPS_NUM_MODES,
+};
+
+/**
* struct ieee80211_conf - configuration of the device
*
* This struct indicates how the driver shall configure the hardware.
@@ -636,6 +653,10 @@ enum ieee80211_conf_changed {
* @short_frame_max_tx_count: Maximum number of transmissions for a "short"
* frame, called "dot11ShortRetryLimit" in 802.11, but actually means the
* number of transmissions not the number of retries
+ *
+ * @smps_mode: spatial multiplexing powersave mode; note that
+ * %IEEE80211_SMPS_STATIC is used when the device is not
+ * configured for an HT channel
*/
struct ieee80211_conf {
u32 flags;
@@ -648,6 +669,7 @@ struct ieee80211_conf {
struct ieee80211_channel *channel;
enum nl80211_channel_type channel_type;
+ enum ieee80211_smps_mode smps_mode;
};
/**
@@ -930,6 +952,16 @@ enum ieee80211_tkip_key_type {
* @IEEE80211_HW_BEACON_FILTER:
* Hardware supports dropping of irrelevant beacon frames to
* avoid waking up cpu.
+ *
+ * @IEEE80211_HW_SUPPORTS_STATIC_SMPS:
+ * Hardware supports static spatial multiplexing powersave,
+ * ie. can turn off all but one chain even on HT connections
+ * that should be using more chains.
+ *
+ * @IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS:
+ * Hardware supports dynamic spatial multiplexing powersave,
+ * ie. can turn off all but one chain and then wake the rest
+ * up as required after, for example, rts/cts handshake.
*/
enum ieee80211_hw_flags {
IEEE80211_HW_HAS_RATE_CONTROL = 1<<0,
@@ -947,6 +979,8 @@ enum ieee80211_hw_flags {
IEEE80211_HW_SUPPORTS_DYNAMIC_PS = 1<<12,
IEEE80211_HW_MFP_CAPABLE = 1<<13,
IEEE80211_HW_BEACON_FILTER = 1<<14,
+ IEEE80211_HW_SUPPORTS_STATIC_SMPS = 1<<15,
+ IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS = 1<<16,
};
/**
@@ -1215,6 +1249,31 @@ ieee80211_get_alt_retry_rate(const struc
*/
/**
+ * DOC: Spatial multiplexing power save
+ *
+ * SMPS (Spatial multiplexing power save) is a mechanism to conserve
+ * power in an 802.11n implementation. For details on the mechanism
+ * and rationale, please refer to 802.11 (as amended by 802.11n-2009)
+ * "11.2.3 SM power save".
+ *
+ * The mac80211 implementation is capable of sending action frames
+ * to update the AP about the station's SMPS mode, and will instruct
+ * the driver to enter the specific mode. It will also announce the
+ * requested SMPS mode during the association handshake. Hardware
+ * support for this feature is required, and can be indicated by
+ * hardware flags.
+ *
+ * The default mode will be "automatic", which nl80211/cfg80211
+ * defines to be dynamic SMPS in (regular) powersave, and SMPS
+ * turned off otherwise.
+ *
+ * To support this feature, the driver must set the appropriate
+ * hardware support flags, and handle the SMPS flag to the config()
+ * operation. It will then with this mechanism be instructed to
+ * enter the requested SMPS mode while associated to an HT AP.
+ */
+
+/**
* DOC: Frame filtering
*
* mac80211 requires to see many management frames for proper
--- wireless-testing.orig/net/mac80211/ieee80211_i.h 2009-12-01 13:16:12.000000000 +0100
+++ wireless-testing/net/mac80211/ieee80211_i.h 2009-12-01 13:16:12.000000000 +0100
@@ -297,6 +297,8 @@ struct ieee80211_if_managed {
unsigned long timers_running; /* used for quiesce/restart */
bool powersave; /* powersave requested for this iface */
+ enum ieee80211_smps_mode req_smps, /* requested smps mode */
+ ap_smps; /* smps mode AP thinks we're in */
unsigned long request;
@@ -587,6 +589,9 @@ struct ieee80211_local {
/* used for uploading changed mc list */
struct work_struct reconfig_filter;
+ /* used to reconfigure hardware SM PS */
+ struct work_struct recalc_smps;
+
/* aggregated multicast list */
struct dev_addr_list *mc_list;
int mc_count;
@@ -760,6 +765,8 @@ struct ieee80211_local {
int user_power_level; /* in dBm */
int power_constr_level; /* in dBm */
+ enum ieee80211_smps_mode smps_mode;
+
struct work_struct restart_work;
#ifdef CONFIG_MAC80211_DEBUGFS
@@ -983,6 +990,9 @@ void ieee80211_send_bar(struct ieee80211
void ieee80211_send_delba(struct ieee80211_sub_if_data *sdata,
const u8 *da, u16 tid,
u16 initiator, u16 reason_code);
+int ieee80211_send_smps_action(struct ieee80211_sub_if_data *sdata,
+ enum ieee80211_smps_mode smps, const u8 *da,
+ const u8 *bssid);
void ieee80211_sta_stop_rx_ba_session(struct ieee80211_sub_if_data *sdata, u8 *da,
u16 tid, u16 initiator, u16 reason);
@@ -1093,6 +1103,10 @@ void ieee80211_sta_def_wmm_params(struct
u32 ieee80211_sta_get_rates(struct ieee80211_local *local,
struct ieee802_11_elems *elems,
enum ieee80211_band band);
+int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata,
+ enum ieee80211_smps_mode smps_mode);
+void ieee80211_recalc_smps(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *forsdata);
#ifdef CONFIG_MAC80211_NOINLINE
#define debug_noinline noinline
--- wireless-testing.orig/net/mac80211/cfg.c 2009-12-01 13:16:12.000000000 +0100
+++ wireless-testing/net/mac80211/cfg.c 2009-12-01 13:16:12.000000000 +0100
@@ -1316,6 +1316,50 @@ static int ieee80211_testmode_cmd(struct
}
#endif
+int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata,
+ enum ieee80211_smps_mode smps_mode)
+{
+ const u8 *ap;
+ enum ieee80211_smps_mode old_req;
+ int err;
+
+ old_req = sdata->u.mgd.req_smps;
+ sdata->u.mgd.req_smps = smps_mode;
+
+ if (old_req == smps_mode &&
+ smps_mode != IEEE80211_SMPS_AUTOMATIC)
+ return 0;
+
+ /*
+ * If not associated, or current association is not an HT
+ * association, there's no need to send an action frame.
+ */
+ if (!sdata->u.mgd.associated ||
+ sdata->local->oper_channel_type == NL80211_CHAN_NO_HT) {
+ mutex_lock(&sdata->local->iflist_mtx);
+ ieee80211_recalc_smps(sdata->local, sdata);
+ mutex_unlock(&sdata->local->iflist_mtx);
+ return 0;
+ }
+
+ ap = sdata->u.mgd.associated->cbss.bssid;
+
+ if (smps_mode == IEEE80211_SMPS_AUTOMATIC) {
+ if (sdata->u.mgd.powersave)
+ smps_mode = IEEE80211_SMPS_DYNAMIC;
+ else
+ smps_mode = IEEE80211_SMPS_OFF;
+ }
+
+ /* send SM PS frame to AP */
+ err = ieee80211_send_smps_action(sdata, smps_mode,
+ ap, ap);
+ if (err)
+ sdata->u.mgd.req_smps = old_req;
+
+ return err;
+}
+
static int ieee80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev,
bool enabled, int timeout)
{
@@ -1333,6 +1377,11 @@ static int ieee80211_set_power_mgmt(stru
sdata->u.mgd.powersave = enabled;
conf->dynamic_ps_timeout = timeout;
+ /* no change, but if automatic follow powersave */
+ mutex_lock(&sdata->u.mgd.mtx);
+ __ieee80211_request_smps(sdata, sdata->u.mgd.req_smps);
+ mutex_unlock(&sdata->u.mgd.mtx);
+
if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS)
ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
--- wireless-testing.orig/include/linux/ieee80211.h 2009-12-01 13:16:12.000000000 +0100
+++ wireless-testing/include/linux/ieee80211.h 2009-12-01 13:16:12.000000000 +0100
@@ -707,6 +707,10 @@ struct ieee80211_mgmt {
u8 action;
u8 trans_id[WLAN_SA_QUERY_TR_ID_LEN];
} __attribute__ ((packed)) sa_query;
+ struct {
+ u8 action;
+ u8 smps_control;
+ } __attribute__ ((packed)) ht_smps;
} u;
} __attribute__ ((packed)) action;
} u;
@@ -824,6 +828,7 @@ struct ieee80211_ht_cap {
#define IEEE80211_HT_CAP_LDPC_CODING 0x0001
#define IEEE80211_HT_CAP_SUP_WIDTH_20_40 0x0002
#define IEEE80211_HT_CAP_SM_PS 0x000C
+#define IEEE80211_HT_CAP_SM_PS_SHIFT 2
#define IEEE80211_HT_CAP_GRN_FLD 0x0010
#define IEEE80211_HT_CAP_SGI_20 0x0020
#define IEEE80211_HT_CAP_SGI_40 0x0040
@@ -839,6 +844,7 @@ struct ieee80211_ht_cap {
/* 802.11n HT capability AMPDU settings (for ampdu_params_info) */
#define IEEE80211_HT_AMPDU_PARM_FACTOR 0x03
#define IEEE80211_HT_AMPDU_PARM_DENSITY 0x1C
+#define IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT 2
/*
* Maximum length of AMPDU that the STA can receive.
@@ -922,12 +928,17 @@ struct ieee80211_ht_info {
#define IEEE80211_MAX_AMPDU_BUF 0x40
-/* Spatial Multiplexing Power Save Modes */
+/* Spatial Multiplexing Power Save Modes (for capability) */
#define WLAN_HT_CAP_SM_PS_STATIC 0
#define WLAN_HT_CAP_SM_PS_DYNAMIC 1
#define WLAN_HT_CAP_SM_PS_INVALID 2
#define WLAN_HT_CAP_SM_PS_DISABLED 3
+/* for SM power control field lower two bits */
+#define WLAN_HT_SMPS_CONTROL_DISABLED 0
+#define WLAN_HT_SMPS_CONTROL_STATIC 1
+#define WLAN_HT_SMPS_CONTROL_DYNAMIC 3
+
/* Authentication algorithms */
#define WLAN_AUTH_OPEN 0
#define WLAN_AUTH_SHARED_KEY 1
@@ -1150,6 +1161,18 @@ enum ieee80211_spectrum_mgmt_actioncode
WLAN_ACTION_SPCT_CHL_SWITCH = 4,
};
+/* HT action codes */
+enum ieee80211_ht_actioncode {
+ WLAN_HT_ACTION_NOTIFY_CHANWIDTH = 0,
+ WLAN_HT_ACTION_SMPS = 1,
+ WLAN_HT_ACTION_PSMP = 2,
+ WLAN_HT_ACTION_PCO_PHASE = 3,
+ WLAN_HT_ACTION_CSI = 4,
+ WLAN_HT_ACTION_NONCOMPRESSED_BF = 5,
+ WLAN_HT_ACTION_COMPRESSED_BF = 6,
+ WLAN_HT_ACTION_ASEL_IDX_FEEDBACK = 7,
+};
+
/* Security key length */
enum ieee80211_key_len {
WLAN_KEY_LEN_WEP40 = 5,
--- wireless-testing.orig/net/mac80211/mlme.c 2009-12-01 13:16:12.000000000 +0100
+++ wireless-testing/net/mac80211/mlme.c 2009-12-01 13:20:50.000000000 +0100
@@ -398,6 +398,8 @@ static void ieee80211_send_assoc(struct
__le16 tmp;
u32 flags = local->hw.conf.channel->flags;
+ /* determine capability flags */
+
switch (ht_info->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
if (flags & IEEE80211_CHAN_NO_HT40PLUS) {
@@ -413,17 +415,64 @@ static void ieee80211_send_assoc(struct
break;
}
- tmp = cpu_to_le16(cap);
- pos = skb_put(skb, sizeof(struct ieee80211_ht_cap)+2);
+ /* set SM PS mode properly */
+ cap &= ~IEEE80211_HT_CAP_SM_PS;
+ /* new association always uses requested smps mode */
+ if (ifmgd->req_smps == IEEE80211_SMPS_AUTOMATIC) {
+ if (ifmgd->powersave)
+ ifmgd->ap_smps = IEEE80211_SMPS_DYNAMIC;
+ else
+ ifmgd->ap_smps = IEEE80211_SMPS_OFF;
+ } else
+ ifmgd->ap_smps = ifmgd->req_smps;
+
+ switch (ifmgd->ap_smps) {
+ case IEEE80211_SMPS_AUTOMATIC:
+ case IEEE80211_SMPS_NUM_MODES:
+ WARN_ON(1);
+ case IEEE80211_SMPS_OFF:
+ cap |= WLAN_HT_CAP_SM_PS_DISABLED <<
+ IEEE80211_HT_CAP_SM_PS_SHIFT;
+ break;
+ case IEEE80211_SMPS_STATIC:
+ cap |= WLAN_HT_CAP_SM_PS_STATIC <<
+ IEEE80211_HT_CAP_SM_PS_SHIFT;
+ break;
+ case IEEE80211_SMPS_DYNAMIC:
+ cap |= WLAN_HT_CAP_SM_PS_DYNAMIC <<
+ IEEE80211_HT_CAP_SM_PS_SHIFT;
+ break;
+ }
+
+ /* reserve and fill IE */
+
+ pos = skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2);
*pos++ = WLAN_EID_HT_CAPABILITY;
*pos++ = sizeof(struct ieee80211_ht_cap);
memset(pos, 0, sizeof(struct ieee80211_ht_cap));
+
+ /* capability flags */
+ tmp = cpu_to_le16(cap);
memcpy(pos, &tmp, sizeof(u16));
pos += sizeof(u16);
- /* TODO: needs a define here for << 2 */
+
+ /* AMPDU parameters */
*pos++ = sband->ht_cap.ampdu_factor |
- (sband->ht_cap.ampdu_density << 2);
+ (sband->ht_cap.ampdu_density <<
+ IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT);
+
+ /* MCS set */
memcpy(pos, &sband->ht_cap.mcs, sizeof(sband->ht_cap.mcs));
+ pos += sizeof(sband->ht_cap.mcs);
+
+ /* extended capabilities */
+ pos += sizeof(__le16);
+
+ /* BF capabilities */
+ pos += sizeof(__le32);
+
+ /* antenna selection */
+ pos += sizeof(u8);
}
IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
@@ -932,6 +981,7 @@ static void ieee80211_set_associated(str
mutex_lock(&local->iflist_mtx);
ieee80211_recalc_ps(local, -1);
+ ieee80211_recalc_smps(local, sdata);
mutex_unlock(&local->iflist_mtx);
netif_start_queue(sdata->dev);
@@ -2328,6 +2378,11 @@ void ieee80211_sta_setup_sdata(struct ie
ifmgd->flags |= IEEE80211_STA_WMM_ENABLED;
mutex_init(&ifmgd->mtx);
+
+ if (sdata->local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS)
+ ifmgd->req_smps = IEEE80211_SMPS_AUTOMATIC;
+ else
+ ifmgd->req_smps = IEEE80211_SMPS_OFF;
}
/* scan finished notification */
--- wireless-testing.orig/net/mac80211/ht.c 2009-12-01 13:16:12.000000000 +0100
+++ wireless-testing/net/mac80211/ht.c 2009-12-01 13:20:46.000000000 +0100
@@ -166,3 +166,50 @@ void ieee80211_process_delba(struct ieee
spin_unlock_bh(&sta->lock);
}
}
+
+int ieee80211_send_smps_action(struct ieee80211_sub_if_data *sdata,
+ enum ieee80211_smps_mode smps, const u8 *da,
+ const u8 *bssid)
+{
+ struct ieee80211_local *local = sdata->local;
+ struct sk_buff *skb;
+ struct ieee80211_mgmt *action_frame;
+
+ /* 27 = header + category + action + smps mode */
+ skb = dev_alloc_skb(27 + local->hw.extra_tx_headroom);
+ if (!skb)
+ return -ENOMEM;
+
+ skb_reserve(skb, local->hw.extra_tx_headroom);
+ action_frame = (void *)skb_put(skb, 27);
+ memcpy(action_frame->da, da, ETH_ALEN);
+ memcpy(action_frame->sa, sdata->dev->dev_addr, ETH_ALEN);
+ memcpy(action_frame->bssid, bssid, ETH_ALEN);
+ action_frame->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
+ IEEE80211_STYPE_ACTION);
+ action_frame->u.action.category = WLAN_CATEGORY_HT;
+ action_frame->u.action.u.ht_smps.action = WLAN_HT_ACTION_SMPS;
+ switch (smps) {
+ case IEEE80211_SMPS_AUTOMATIC:
+ case IEEE80211_SMPS_NUM_MODES:
+ WARN_ON(1);
+ case IEEE80211_SMPS_OFF:
+ action_frame->u.action.u.ht_smps.smps_control =
+ WLAN_HT_SMPS_CONTROL_DISABLED;
+ break;
+ case IEEE80211_SMPS_STATIC:
+ action_frame->u.action.u.ht_smps.smps_control =
+ WLAN_HT_SMPS_CONTROL_STATIC;
+ break;
+ case IEEE80211_SMPS_DYNAMIC:
+ action_frame->u.action.u.ht_smps.smps_control =
+ WLAN_HT_SMPS_CONTROL_DYNAMIC;
+ break;
+ }
+
+ /* we'll do more on status of this frame */
+ IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS;
+ ieee80211_tx_skb(sdata, skb);
+
+ return 0;
+}
--- wireless-testing.orig/net/mac80211/status.c 2009-12-01 13:16:12.000000000 +0100
+++ wireless-testing/net/mac80211/status.c 2009-12-01 13:16:12.000000000 +0100
@@ -134,6 +134,40 @@ static void ieee80211_handle_filtered_fr
dev_kfree_skb(skb);
}
+static void ieee80211_frame_acked(struct sta_info *sta, struct sk_buff *skb)
+{
+ struct ieee80211_mgmt *mgmt = (void *) skb->data;
+ struct ieee80211_local *local = sta->local;
+ struct ieee80211_sub_if_data *sdata = sta->sdata;
+
+ if (ieee80211_is_action(mgmt->frame_control) &&
+ sdata->vif.type == NL80211_IFTYPE_STATION &&
+ mgmt->u.action.category == WLAN_CATEGORY_HT &&
+ mgmt->u.action.u.ht_smps.action == WLAN_HT_ACTION_SMPS) {
+ /*
+ * This update looks racy, but isn't -- if we come
+ * here we've definitely got a station that we're
+ * talking to, and on a managed interface that can
+ * only be the AP. And the only other place updating
+ * this variable is before we're associated.
+ */
+ switch (mgmt->u.action.u.ht_smps.smps_control) {
+ case WLAN_HT_SMPS_CONTROL_DYNAMIC:
+ sta->sdata->u.mgd.ap_smps = IEEE80211_SMPS_DYNAMIC;
+ break;
+ case WLAN_HT_SMPS_CONTROL_STATIC:
+ sta->sdata->u.mgd.ap_smps = IEEE80211_SMPS_STATIC;
+ break;
+ case WLAN_HT_SMPS_CONTROL_DISABLED:
+ default: /* shouldn't happen since we don't send that */
+ sta->sdata->u.mgd.ap_smps = IEEE80211_SMPS_OFF;
+ break;
+ }
+
+ ieee80211_queue_work(&local->hw, &local->recalc_smps);
+ }
+}
+
void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
{
struct sk_buff *skb2;
@@ -209,6 +243,10 @@ void ieee80211_tx_status(struct ieee8021
rate_control_tx_status(local, sband, sta, skb);
if (ieee80211_vif_is_mesh(&sta->sdata->vif))
ieee80211s_update_metric(local, sta, skb);
+
+ if (!(info->flags & IEEE80211_TX_CTL_INJECTED) &&
+ (info->flags & IEEE80211_TX_STAT_ACK))
+ ieee80211_frame_acked(sta, skb);
}
rcu_read_unlock();
--- wireless-testing.orig/net/mac80211/main.c 2009-12-01 13:16:12.000000000 +0100
+++ wireless-testing/net/mac80211/main.c 2009-12-01 13:16:12.000000000 +0100
@@ -113,6 +113,18 @@ int ieee80211_hw_config(struct ieee80211
changed |= IEEE80211_CONF_CHANGE_CHANNEL;
}
+ if (!conf_is_ht(&local->hw.conf)) {
+ /*
+ * mac80211.h documents that this is only valid
+ * when the channel is set to an HT type, and
+ * that otherwise STATIC is used.
+ */
+ local->hw.conf.smps_mode = IEEE80211_SMPS_STATIC;
+ } else if (local->hw.conf.smps_mode != local->smps_mode) {
+ local->hw.conf.smps_mode = local->smps_mode;
+ changed |= IEEE80211_CONF_CHANGE_SMPS;
+ }
+
if (scan_chan)
power = chan->max_power;
else
@@ -297,6 +309,16 @@ void ieee80211_restart_hw(struct ieee802
}
EXPORT_SYMBOL(ieee80211_restart_hw);
+static void ieee80211_recalc_smps_work(struct work_struct *work)
+{
+ struct ieee80211_local *local =
+ container_of(work, struct ieee80211_local, recalc_smps);
+
+ mutex_lock(&local->iflist_mtx);
+ ieee80211_recalc_smps(local, NULL);
+ mutex_unlock(&local->iflist_mtx);
+}
+
struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
const struct ieee80211_ops *ops)
{
@@ -370,6 +392,8 @@ struct ieee80211_hw *ieee80211_alloc_hw(
INIT_WORK(&local->restart_work, ieee80211_restart_work);
INIT_WORK(&local->reconfig_filter, ieee80211_reconfig_filter);
+ INIT_WORK(&local->recalc_smps, ieee80211_recalc_smps_work);
+ local->smps_mode = IEEE80211_SMPS_OFF;
INIT_WORK(&local->dynamic_ps_enable_work,
ieee80211_dynamic_ps_enable_work);
--- wireless-testing.orig/net/mac80211/util.c 2009-12-01 13:16:12.000000000 +0100
+++ wireless-testing/net/mac80211/util.c 2009-12-01 13:16:12.000000000 +0100
@@ -1170,3 +1170,77 @@ int ieee80211_reconfig(struct ieee80211_
return 0;
}
+static int check_mgd_smps(struct ieee80211_if_managed *ifmgd,
+ enum ieee80211_smps_mode *smps_mode)
+{
+ if (ifmgd->associated) {
+ *smps_mode = ifmgd->ap_smps;
+
+ if (*smps_mode == IEEE80211_SMPS_AUTOMATIC) {
+ if (ifmgd->powersave)
+ *smps_mode = IEEE80211_SMPS_DYNAMIC;
+ else
+ *smps_mode = IEEE80211_SMPS_OFF;
+ }
+
+ return 1;
+ }
+
+ return 0;
+}
+
+/* must hold iflist_mtx */
+void ieee80211_recalc_smps(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *forsdata)
+{
+ struct ieee80211_sub_if_data *sdata;
+ enum ieee80211_smps_mode smps_mode = IEEE80211_SMPS_OFF;
+ int count = 0;
+
+ if (forsdata)
+ WARN_ON(!mutex_is_locked(&forsdata->u.mgd.mtx));
+
+ WARN_ON(!mutex_is_locked(&local->iflist_mtx));
+
+ /*
+ * This function could be improved to handle multiple
+ * interfaces better, but right now it makes any
+ * non-station interfaces force SM PS to be turned
+ * off. If there are multiple station interfaces it
+ * could also use the best possible mode, e.g. if
+ * one is in static and the other in dynamic then
+ * dynamic is ok.
+ */
+
+ list_for_each_entry(sdata, &local->interfaces, list) {
+ if (!netif_running(sdata->dev))
+ continue;
+ if (sdata->vif.type != NL80211_IFTYPE_STATION)
+ goto set;
+ if (sdata != forsdata) {
+ /*
+ * This nested is ok -- we are holding the iflist_mtx
+ * so can't get here twice or so. But it's required
+ * since normally we acquire it first and then the
+ * iflist_mtx.
+ */
+ mutex_lock_nested(&sdata->u.mgd.mtx, SINGLE_DEPTH_NESTING);
+ count += check_mgd_smps(&sdata->u.mgd, &smps_mode);
+ mutex_unlock(&sdata->u.mgd.mtx);
+ } else
+ count += check_mgd_smps(&sdata->u.mgd, &smps_mode);
+
+ if (count > 1) {
+ smps_mode = IEEE80211_SMPS_OFF;
+ break;
+ }
+ }
+
+ if (smps_mode == local->smps_mode)
+ return;
+
+ set:
+ local->smps_mode = smps_mode;
+ /* changed flag is auto-detected for this */
+ ieee80211_hw_config(local, 0);
+}
--- wireless-testing.orig/drivers/net/wireless/mac80211_hwsim.c 2009-12-01 13:16:12.000000000 +0100
+++ wireless-testing/drivers/net/wireless/mac80211_hwsim.c 2009-12-01 13:21:17.000000000 +0100
@@ -619,12 +619,26 @@ static int mac80211_hwsim_config(struct
{
struct mac80211_hwsim_data *data = hw->priv;
struct ieee80211_conf *conf = &hw->conf;
+ static const char *chantypes[4] = {
+ [NL80211_CHAN_NO_HT] = "noht",
+ [NL80211_CHAN_HT20] = "ht20",
+ [NL80211_CHAN_HT40MINUS] = "ht40-",
+ [NL80211_CHAN_HT40PLUS] = "ht40+",
+ };
+ static const char *smps_modes[IEEE80211_SMPS_NUM_MODES] = {
+ [IEEE80211_SMPS_AUTOMATIC] = "auto",
+ [IEEE80211_SMPS_OFF] = "off",
+ [IEEE80211_SMPS_STATIC] = "static",
+ [IEEE80211_SMPS_DYNAMIC] = "dynamic",
+ };
- printk(KERN_DEBUG "%s:%s (freq=%d idle=%d ps=%d)\n",
+ printk(KERN_DEBUG "%s:%s (freq=%d/%s idle=%d ps=%d smps=%s)\n",
wiphy_name(hw->wiphy), __func__,
conf->channel->center_freq,
+ chantypes[conf->channel_type],
!!(conf->flags & IEEE80211_CONF_IDLE),
- !!(conf->flags & IEEE80211_CONF_PS));
+ !!(conf->flags & IEEE80211_CONF_PS),
+ smps_modes[conf->smps_mode]);
data->idle = !!(conf->flags & IEEE80211_CONF_IDLE);
@@ -1121,7 +1135,9 @@ static int __init init_mac80211_hwsim(vo
BIT(NL80211_IFTYPE_MESH_POINT);
hw->flags = IEEE80211_HW_MFP_CAPABLE |
- IEEE80211_HW_SIGNAL_DBM;
+ IEEE80211_HW_SIGNAL_DBM |
+ IEEE80211_HW_SUPPORTS_STATIC_SMPS |
+ IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS;
/* ask mac80211 to reserve space for magic */
hw->vif_data_size = sizeof(struct hwsim_vif_priv);
--- wireless-testing.orig/net/mac80211/driver-trace.h 2009-12-01 13:16:12.000000000 +0100
+++ wireless-testing/net/mac80211/driver-trace.h 2009-12-01 13:16:12.000000000 +0100
@@ -140,6 +140,7 @@ TRACE_EVENT(drv_config,
__field(u8, short_frame_max_tx_count)
__field(int, center_freq)
__field(int, channel_type)
+ __field(int, smps)
),
TP_fast_assign(
@@ -155,6 +156,7 @@ TRACE_EVENT(drv_config,
__entry->short_frame_max_tx_count = local->hw.conf.short_frame_max_tx_count;
__entry->center_freq = local->hw.conf.channel->center_freq;
__entry->channel_type = local->hw.conf.channel_type;
+ __entry->smps = local->hw.conf.smps_mode;
),
TP_printk(
--- wireless-testing.orig/net/mac80211/debugfs_netdev.c 2009-12-01 13:16:12.000000000 +0100
+++ wireless-testing/net/mac80211/debugfs_netdev.c 2009-12-01 13:32:52.000000000 +0100
@@ -41,6 +41,30 @@ static ssize_t ieee80211_if_read(
return ret;
}
+static ssize_t ieee80211_if_write(
+ struct ieee80211_sub_if_data *sdata,
+ const char __user *userbuf,
+ size_t count, loff_t *ppos,
+ ssize_t (*write)(struct ieee80211_sub_if_data *, const char *, int))
+{
+ u8 *buf;
+ ssize_t ret = -ENODEV;
+
+ buf = kzalloc(count, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ if (copy_from_user(buf, userbuf, count))
+ return -EFAULT;
+
+ rtnl_lock();
+ if (sdata->dev->reg_state == NETREG_REGISTERED)
+ ret = (*write)(sdata, buf, count);
+ rtnl_unlock();
+
+ return ret;
+}
+
#define IEEE80211_IF_FMT(name, field, format_string) \
static ssize_t ieee80211_if_fmt_##name( \
const struct ieee80211_sub_if_data *sdata, char *buf, \
@@ -71,7 +95,7 @@ static ssize_t ieee80211_if_fmt_##name(
return scnprintf(buf, buflen, "%pM\n", sdata->field); \
}
-#define __IEEE80211_IF_FILE(name) \
+#define __IEEE80211_IF_FILE(name, _write) \
static ssize_t ieee80211_if_read_##name(struct file *file, \
char __user *userbuf, \
size_t count, loff_t *ppos) \
@@ -82,12 +106,24 @@ static ssize_t ieee80211_if_read_##name(
} \
static const struct file_operations name##_ops = { \
.read = ieee80211_if_read_##name, \
+ .write = (_write), \
.open = mac80211_open_file_generic, \
}
+#define __IEEE80211_IF_FILE_W(name) \
+static ssize_t ieee80211_if_write_##name(struct file *file, \
+ const char __user *userbuf, \
+ size_t count, loff_t *ppos) \
+{ \
+ return ieee80211_if_write(file->private_data, userbuf, count, \
+ ppos, ieee80211_if_parse_##name); \
+} \
+__IEEE80211_IF_FILE(name, ieee80211_if_write_##name)
+
+
#define IEEE80211_IF_FILE(name, field, format) \
IEEE80211_IF_FMT_##format(name, field) \
- __IEEE80211_IF_FILE(name)
+ __IEEE80211_IF_FILE(name, NULL)
/* common attributes */
IEEE80211_IF_FILE(drop_unencrypted, drop_unencrypted, DEC);
@@ -99,6 +135,70 @@ IEEE80211_IF_FILE(bssid, u.mgd.bssid, MA
IEEE80211_IF_FILE(aid, u.mgd.aid, DEC);
IEEE80211_IF_FILE(capab, u.mgd.capab, HEX);
+static int ieee80211_set_smps(struct ieee80211_sub_if_data *sdata,
+ enum ieee80211_smps_mode smps_mode)
+{
+ struct ieee80211_local *local = sdata->local;
+ int err;
+
+ if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_STATIC_SMPS) &&
+ smps_mode == IEEE80211_SMPS_STATIC)
+ return -EINVAL;
+
+ /* auto should be dynamic if in PS mode */
+ if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS) &&
+ (smps_mode == IEEE80211_SMPS_DYNAMIC ||
+ smps_mode == IEEE80211_SMPS_AUTOMATIC))
+ return -EINVAL;
+
+ /* supported only on managed interfaces for now */
+ if (sdata->vif.type != NL80211_IFTYPE_STATION)
+ return -EOPNOTSUPP;
+
+ mutex_lock(&local->iflist_mtx);
+ err = __ieee80211_request_smps(sdata, smps_mode);
+ mutex_unlock(&local->iflist_mtx);
+
+ return err;
+}
+
+static const char *smps_modes[IEEE80211_SMPS_NUM_MODES] = {
+ [IEEE80211_SMPS_AUTOMATIC] = "auto",
+ [IEEE80211_SMPS_OFF] = "off",
+ [IEEE80211_SMPS_STATIC] = "static",
+ [IEEE80211_SMPS_DYNAMIC] = "dynamic",
+};
+
+static ssize_t ieee80211_if_fmt_smps(const struct ieee80211_sub_if_data *sdata,
+ char *buf, int buflen)
+{
+ if (sdata->vif.type != NL80211_IFTYPE_STATION)
+ return -EOPNOTSUPP;
+
+ return snprintf(buf, buflen, "request: %s\nused: %s\n",
+ smps_modes[sdata->u.mgd.req_smps],
+ smps_modes[sdata->u.mgd.ap_smps]);
+}
+
+static ssize_t ieee80211_if_parse_smps(struct ieee80211_sub_if_data *sdata,
+ const char *buf, int buflen)
+{
+ enum ieee80211_smps_mode mode;
+
+ for (mode = 0; mode < IEEE80211_SMPS_NUM_MODES; mode++) {
+ if (strncmp(buf, smps_modes[mode], buflen) == 0) {
+ int err = ieee80211_set_smps(sdata, mode);
+ if (!err)
+ return buflen;
+ return err;
+ }
+ }
+
+ return -EINVAL;
+}
+
+__IEEE80211_IF_FILE_W(smps);
+
/* AP attributes */
IEEE80211_IF_FILE(num_sta_ps, u.ap.num_sta_ps, ATOMIC);
IEEE80211_IF_FILE(dtim_count, u.ap.dtim_count, DEC);
@@ -109,7 +209,7 @@ static ssize_t ieee80211_if_fmt_num_buff
return scnprintf(buf, buflen, "%u\n",
skb_queue_len(&sdata->u.ap.ps_bc_buf));
}
-__IEEE80211_IF_FILE(num_buffered_multicast);
+__IEEE80211_IF_FILE(num_buffered_multicast, NULL);
/* WDS attributes */
IEEE80211_IF_FILE(peer, u.wds.remote_addr, MAC);
@@ -158,6 +258,10 @@ IEEE80211_IF_FILE(dot11MeshHWMPRootMode,
debugfs_create_file(#name, 0400, sdata->debugfs.dir, \
sdata, &name##_ops);
+#define DEBUGFS_ADD_MODE(name, mode) \
+ debugfs_create_file(#name, mode, sdata->debugfs.dir, \
+ sdata, &name##_ops);
+
static void add_sta_files(struct ieee80211_sub_if_data *sdata)
{
DEBUGFS_ADD(drop_unencrypted, sta);
@@ -167,6 +271,7 @@ static void add_sta_files(struct ieee802
DEBUGFS_ADD(bssid, sta);
DEBUGFS_ADD(aid, sta);
DEBUGFS_ADD(capab, sta);
+ DEBUGFS_ADD_MODE(smps, 0600);
}
static void add_ap_files(struct ieee80211_sub_if_data *sdata)
^ permalink raw reply [flat|nested] 12+ messages in thread
end of thread, other threads:[~2009-12-01 12:37 UTC | newest]
Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2009-11-30 15:38 [PATCH 0/2] Spatial multiplexing powersave (client support) Johannes Berg
2009-11-30 15:38 ` [PATCH 1/2] cfg80211: add SM PS hooks Johannes Berg
2009-12-01 7:01 ` Kalle Valo
2009-12-01 9:29 ` Johannes Berg
2009-12-01 11:44 ` Kalle Valo
2009-11-30 15:38 ` [PATCH 2/2] mac80211: enable spatial multiplexing powersave Johannes Berg
2009-11-30 16:54 ` Luis R. Rodriguez
2009-11-30 17:04 ` Johannes Berg
2009-11-30 17:22 ` Luis R. Rodriguez
2009-11-30 17:37 ` Johannes Berg
2009-11-30 17:15 ` [PATCH 2/2 v2] " Johannes Berg
2009-12-01 12:37 ` [PATCH v3] " Johannes Berg
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).