Commit 4be8c387 authored by Johannes Berg's avatar Johannes Berg Committed by John W. Linville

mac80211: extend/document powersave API

This modifies hardware flags for powersave to support three different
flags:
 * IEEE80211_HW_SUPPORTS_PS - indicates general PS support
 * IEEE80211_HW_PS_NULLFUNC_STACK - indicates nullfunc sending in software
 * IEEE80211_HW_SUPPORTS_DYNAMIC_PS - indicates dynamic PS on the device

It also adds documentation for all this which explains how to set the
various flags.

Additionally, it fixes a few things:
 * a spot where && was used to test flags
 * enable CONF_PS only when associated again
Signed-off-by: default avatarJohannes Berg <johannes@sipsolutions.net>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent acbaf32e
...@@ -17,8 +17,7 @@ ...@@ -17,8 +17,7 @@
</authorgroup> </authorgroup>
<copyright> <copyright>
<year>2007</year> <year>2007-2009</year>
<year>2008</year>
<holder>Johannes Berg</holder> <holder>Johannes Berg</holder>
</copyright> </copyright>
...@@ -223,6 +222,11 @@ usage should require reading the full document. ...@@ -223,6 +222,11 @@ usage should require reading the full document.
!Finclude/net/mac80211.h ieee80211_key_flags !Finclude/net/mac80211.h ieee80211_key_flags
</chapter> </chapter>
<chapter id="powersave">
<title>Powersave support</title>
!Pinclude/net/mac80211.h Powersave support
</chapter>
<chapter id="qos"> <chapter id="qos">
<title>Multiple queues and QoS support</title> <title>Multiple queues and QoS support</title>
<para>TBD</para> <para>TBD</para>
......
...@@ -805,7 +805,8 @@ int iwl_setup_mac(struct iwl_priv *priv) ...@@ -805,7 +805,8 @@ int iwl_setup_mac(struct iwl_priv *priv)
/* Tell mac80211 our characteristics */ /* Tell mac80211 our characteristics */
hw->flags = IEEE80211_HW_SIGNAL_DBM | hw->flags = IEEE80211_HW_SIGNAL_DBM |
IEEE80211_HW_NOISE_DBM | IEEE80211_HW_NOISE_DBM |
IEEE80211_HW_AMPDU_AGGREGATION; IEEE80211_HW_AMPDU_AGGREGATION |
IEEE80211_HW_SUPPORTS_PS;
hw->wiphy->interface_modes = hw->wiphy->interface_modes =
BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_STATION) |
BIT(NL80211_IFTYPE_ADHOC); BIT(NL80211_IFTYPE_ADHOC);
......
...@@ -1449,7 +1449,9 @@ static int rt2400pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev) ...@@ -1449,7 +1449,9 @@ static int rt2400pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
* Initialize all hw fields. * Initialize all hw fields.
*/ */
rt2x00dev->hw->flags = IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | rt2x00dev->hw->flags = IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING |
IEEE80211_HW_SIGNAL_DBM; IEEE80211_HW_SIGNAL_DBM |
IEEE80211_HW_SUPPORTS_PS |
IEEE80211_HW_PS_NULLFUNC_STACK;
rt2x00dev->hw->extra_tx_headroom = 0; rt2x00dev->hw->extra_tx_headroom = 0;
SET_IEEE80211_DEV(rt2x00dev->hw, rt2x00dev->dev); SET_IEEE80211_DEV(rt2x00dev->hw, rt2x00dev->dev);
......
...@@ -1749,7 +1749,9 @@ static int rt2500pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev) ...@@ -1749,7 +1749,9 @@ static int rt2500pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
* Initialize all hw fields. * Initialize all hw fields.
*/ */
rt2x00dev->hw->flags = IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | rt2x00dev->hw->flags = IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING |
IEEE80211_HW_SIGNAL_DBM; IEEE80211_HW_SIGNAL_DBM |
IEEE80211_HW_SUPPORTS_PS |
IEEE80211_HW_PS_NULLFUNC_STACK;
rt2x00dev->hw->extra_tx_headroom = 0; rt2x00dev->hw->extra_tx_headroom = 0;
......
...@@ -1801,7 +1801,9 @@ static int rt2500usb_probe_hw_mode(struct rt2x00_dev *rt2x00dev) ...@@ -1801,7 +1801,9 @@ static int rt2500usb_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
rt2x00dev->hw->flags = rt2x00dev->hw->flags =
IEEE80211_HW_RX_INCLUDES_FCS | IEEE80211_HW_RX_INCLUDES_FCS |
IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING |
IEEE80211_HW_SIGNAL_DBM; IEEE80211_HW_SIGNAL_DBM |
IEEE80211_HW_SUPPORTS_PS |
IEEE80211_HW_PS_NULLFUNC_STACK;
rt2x00dev->hw->extra_tx_headroom = TXD_DESC_SIZE; rt2x00dev->hw->extra_tx_headroom = TXD_DESC_SIZE;
......
...@@ -2556,7 +2556,9 @@ static int rt61pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev) ...@@ -2556,7 +2556,9 @@ static int rt61pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
*/ */
rt2x00dev->hw->flags = rt2x00dev->hw->flags =
IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING |
IEEE80211_HW_SIGNAL_DBM; IEEE80211_HW_SIGNAL_DBM |
IEEE80211_HW_SUPPORTS_PS |
IEEE80211_HW_PS_NULLFUNC_STACK;
rt2x00dev->hw->extra_tx_headroom = 0; rt2x00dev->hw->extra_tx_headroom = 0;
SET_IEEE80211_DEV(rt2x00dev->hw, rt2x00dev->dev); SET_IEEE80211_DEV(rt2x00dev->hw, rt2x00dev->dev);
......
...@@ -2077,7 +2077,9 @@ static int rt73usb_probe_hw_mode(struct rt2x00_dev *rt2x00dev) ...@@ -2077,7 +2077,9 @@ static int rt73usb_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
*/ */
rt2x00dev->hw->flags = rt2x00dev->hw->flags =
IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING |
IEEE80211_HW_SIGNAL_DBM; IEEE80211_HW_SIGNAL_DBM |
IEEE80211_HW_SUPPORTS_PS |
IEEE80211_HW_PS_NULLFUNC_STACK;
rt2x00dev->hw->extra_tx_headroom = TXD_DESC_SIZE; rt2x00dev->hw->extra_tx_headroom = TXD_DESC_SIZE;
SET_IEEE80211_DEV(rt2x00dev->hw, rt2x00dev->dev); SET_IEEE80211_DEV(rt2x00dev->hw, rt2x00dev->dev);
......
...@@ -850,10 +850,15 @@ enum ieee80211_tkip_key_type { ...@@ -850,10 +850,15 @@ enum ieee80211_tkip_key_type {
* @IEEE80211_HW_AMPDU_AGGREGATION: * @IEEE80211_HW_AMPDU_AGGREGATION:
* Hardware supports 11n A-MPDU aggregation. * Hardware supports 11n A-MPDU aggregation.
* *
* @IEEE80211_HW_NO_STACK_DYNAMIC_PS: * @IEEE80211_HW_SUPPORTS_PS:
* Hardware which has dynamic power save support, meaning * Hardware has power save support (i.e. can go to sleep).
* that power save is enabled in idle periods, and don't need support *
* from stack. * @IEEE80211_HW_PS_NULLFUNC_STACK:
* Hardware requires nullfunc frame handling in stack, implies
* stack support for dynamic PS.
*
* @IEEE80211_HW_SUPPORTS_DYNAMIC_PS:
* Hardware has support for dynamic PS.
*/ */
enum ieee80211_hw_flags { enum ieee80211_hw_flags {
IEEE80211_HW_RX_INCLUDES_FCS = 1<<1, IEEE80211_HW_RX_INCLUDES_FCS = 1<<1,
...@@ -866,7 +871,9 @@ enum ieee80211_hw_flags { ...@@ -866,7 +871,9 @@ enum ieee80211_hw_flags {
IEEE80211_HW_NOISE_DBM = 1<<8, IEEE80211_HW_NOISE_DBM = 1<<8,
IEEE80211_HW_SPECTRUM_MGMT = 1<<9, IEEE80211_HW_SPECTRUM_MGMT = 1<<9,
IEEE80211_HW_AMPDU_AGGREGATION = 1<<10, IEEE80211_HW_AMPDU_AGGREGATION = 1<<10,
IEEE80211_HW_NO_STACK_DYNAMIC_PS = 1<<11, IEEE80211_HW_SUPPORTS_PS = 1<<11,
IEEE80211_HW_PS_NULLFUNC_STACK = 1<<12,
IEEE80211_HW_SUPPORTS_DYNAMIC_PS = 1<<13,
}; };
/** /**
...@@ -1052,6 +1059,42 @@ ieee80211_get_alt_retry_rate(const struct ieee80211_hw *hw, ...@@ -1052,6 +1059,42 @@ ieee80211_get_alt_retry_rate(const struct ieee80211_hw *hw,
* handler is software decryption with wrap around of iv16. * handler is software decryption with wrap around of iv16.
*/ */
/**
* DOC: Powersave support
*
* mac80211 has support for various powersave implementations.
*
* First, it can support hardware that handles all powersaving by
* itself, such hardware should simply set the %IEEE80211_HW_SUPPORTS_PS
* hardware flag. In that case, it will be told about the desired
* powersave mode depending on the association status, and the driver
* must take care of sending nullfunc frames when necessary, i.e. when
* entering and leaving powersave mode. The driver is required to look at
* the AID in beacons and signal to the AP that it woke up when it finds
* traffic directed to it. This mode supports dynamic PS by simply
* enabling/disabling PS.
*
* Additionally, such hardware may set the %IEEE80211_HW_SUPPORTS_DYNAMIC_PS
* flag to indicate that it can support dynamic PS mode itself (see below).
*
* Other hardware designs cannot send nullfunc frames by themselves and also
* need software support for parsing the TIM bitmap. This is also supported
* by mac80211 by combining the %IEEE80211_HW_SUPPORTS_PS and
* %IEEE80211_HW_PS_NULLFUNC_STACK flags. The hardware is of course still
* required to pass up beacons. Additionally, in this case, mac80211 will
* wake up the hardware when multicast traffic is announced in the beacon.
*
* FIXME: I don't think we can be fast enough in software when we want to
* receive multicast traffic?
*
* Dynamic powersave mode is an extension to normal powersave mode in which
* the hardware stays awake for a user-specified period of time after sending
* a frame so that reply frames need not be buffered and therefore delayed
* to the next wakeup. This can either be supported by hardware, in which case
* the driver needs to look at the @dynamic_ps_timeout hardware configuration
* value, or by the stack if all nullfunc handling is in the stack.
*/
/** /**
* DOC: Frame filtering * DOC: Frame filtering
* *
......
...@@ -775,17 +775,17 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, ...@@ -775,17 +775,17 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
bss_info_changed |= BSS_CHANGED_BASIC_RATES; bss_info_changed |= BSS_CHANGED_BASIC_RATES;
ieee80211_bss_info_change_notify(sdata, bss_info_changed); ieee80211_bss_info_change_notify(sdata, bss_info_changed);
if (local->powersave && if (local->powersave) {
!(local->hw.flags & IEEE80211_HW_NO_STACK_DYNAMIC_PS)) { if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS) &&
if (local->hw.conf.dynamic_ps_timeout > 0) local->hw.conf.dynamic_ps_timeout > 0) {
mod_timer(&local->dynamic_ps_timer, jiffies + mod_timer(&local->dynamic_ps_timer, jiffies +
msecs_to_jiffies( msecs_to_jiffies(
local->hw.conf.dynamic_ps_timeout)); local->hw.conf.dynamic_ps_timeout));
else { } else {
ieee80211_send_nullfunc(local, sdata, 1); if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)
ieee80211_send_nullfunc(local, sdata, 1);
conf->flags |= IEEE80211_CONF_PS; conf->flags |= IEEE80211_CONF_PS;
ieee80211_hw_config(local, ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
IEEE80211_CONF_CHANGE_PS);
} }
} }
...@@ -1779,16 +1779,14 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, ...@@ -1779,16 +1779,14 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
ieee80211_sta_wmm_params(local, ifsta, elems.wmm_param, ieee80211_sta_wmm_params(local, ifsta, elems.wmm_param,
elems.wmm_param_len); elems.wmm_param_len);
if (!(local->hw.flags & IEEE80211_HW_NO_STACK_DYNAMIC_PS)) { if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK &&
local->hw.conf.flags & IEEE80211_CONF_PS) {
directed_tim = check_tim(&elems, ifsta->aid, &is_mc); directed_tim = check_tim(&elems, ifsta->aid, &is_mc);
if (directed_tim || is_mc) { if (directed_tim || is_mc) {
if (local->hw.conf.flags && IEEE80211_CONF_PS) { local->hw.conf.flags &= ~IEEE80211_CONF_PS;
local->hw.conf.flags &= ~IEEE80211_CONF_PS; ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
ieee80211_hw_config(local, ieee80211_send_nullfunc(local, sdata, 0);
IEEE80211_CONF_CHANGE_PS);
ieee80211_send_nullfunc(local, sdata, 0);
}
} }
} }
...@@ -2694,9 +2692,10 @@ void ieee80211_dynamic_ps_enable_work(struct work_struct *work) ...@@ -2694,9 +2692,10 @@ void ieee80211_dynamic_ps_enable_work(struct work_struct *work)
if (local->hw.conf.flags & IEEE80211_CONF_PS) if (local->hw.conf.flags & IEEE80211_CONF_PS)
return; return;
ieee80211_send_nullfunc(local, sdata, 1); if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)
local->hw.conf.flags |= IEEE80211_CONF_PS; ieee80211_send_nullfunc(local, sdata, 1);
local->hw.conf.flags |= IEEE80211_CONF_PS;
ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS); ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
} }
......
...@@ -1295,7 +1295,7 @@ int ieee80211_master_start_xmit(struct sk_buff *skb, struct net_device *dev) ...@@ -1295,7 +1295,7 @@ int ieee80211_master_start_xmit(struct sk_buff *skb, struct net_device *dev)
return 0; return 0;
} }
if (!(local->hw.flags & IEEE80211_HW_NO_STACK_DYNAMIC_PS) && if ((local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) &&
local->hw.conf.dynamic_ps_timeout > 0) { local->hw.conf.dynamic_ps_timeout > 0) {
if (local->hw.conf.flags & IEEE80211_CONF_PS) { if (local->hw.conf.flags & IEEE80211_CONF_PS) {
ieee80211_stop_queues_by_reason(&local->hw, ieee80211_stop_queues_by_reason(&local->hw,
......
...@@ -837,6 +837,9 @@ static int ieee80211_ioctl_siwpower(struct net_device *dev, ...@@ -837,6 +837,9 @@ static int ieee80211_ioctl_siwpower(struct net_device *dev,
int ret = 0, timeout = 0; int ret = 0, timeout = 0;
bool ps; bool ps;
if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_PS))
return -EOPNOTSUPP;
if (sdata->vif.type != NL80211_IFTYPE_STATION) if (sdata->vif.type != NL80211_IFTYPE_STATION)
return -EINVAL; return -EINVAL;
...@@ -862,32 +865,37 @@ static int ieee80211_ioctl_siwpower(struct net_device *dev, ...@@ -862,32 +865,37 @@ static int ieee80211_ioctl_siwpower(struct net_device *dev,
if (wrq->flags & IW_POWER_TIMEOUT) if (wrq->flags & IW_POWER_TIMEOUT)
timeout = wrq->value / 1000; timeout = wrq->value / 1000;
set: set:
if (ps == local->powersave && timeout == conf->dynamic_ps_timeout) if (ps == local->powersave && timeout == conf->dynamic_ps_timeout)
return ret; return ret;
local->powersave = ps; local->powersave = ps;
conf->dynamic_ps_timeout = timeout; conf->dynamic_ps_timeout = timeout;
if (local->hw.flags & IEEE80211_HW_NO_STACK_DYNAMIC_PS) { if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS)
ret = ieee80211_hw_config(local, ret = ieee80211_hw_config(local,
IEEE80211_CONF_CHANGE_DYNPS_TIMEOUT); IEEE80211_CONF_CHANGE_DYNPS_TIMEOUT);
} else if (sdata->u.sta.flags & IEEE80211_STA_ASSOCIATED) {
if (conf->dynamic_ps_timeout > 0) if (!(sdata->u.sta.flags & IEEE80211_STA_ASSOCIATED))
mod_timer(&local->dynamic_ps_timer, jiffies + return ret;
msecs_to_jiffies(conf->dynamic_ps_timeout));
else { if (conf->dynamic_ps_timeout > 0 &&
if (local->powersave) { !(local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS)) {
mod_timer(&local->dynamic_ps_timer, jiffies +
msecs_to_jiffies(conf->dynamic_ps_timeout));
} else {
if (local->powersave) {
if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)
ieee80211_send_nullfunc(local, sdata, 1); ieee80211_send_nullfunc(local, sdata, 1);
conf->flags |= IEEE80211_CONF_PS; conf->flags |= IEEE80211_CONF_PS;
ret = ieee80211_hw_config(local, ret = ieee80211_hw_config(local,
IEEE80211_CONF_CHANGE_PS); IEEE80211_CONF_CHANGE_PS);
} else { } else {
conf->flags &= ~IEEE80211_CONF_PS; conf->flags &= ~IEEE80211_CONF_PS;
ret = ieee80211_hw_config(local, ret = ieee80211_hw_config(local,
IEEE80211_CONF_CHANGE_PS); IEEE80211_CONF_CHANGE_PS);
if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)
ieee80211_send_nullfunc(local, sdata, 0); ieee80211_send_nullfunc(local, sdata, 0);
}
} }
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment